测试是否存在与模式匹配的文件以执行脚本

测试是否存在与模式匹配的文件以执行脚本

我正在尝试编写一条if语句来测试是否有任何文件与特定模式匹配。如果目录中有文本文件,它应该运行给定的脚本。

我目前的代码:

if [ -f /*.txt ]; then ./script fi

请给一些想法;我只想.txt在目录中有 a 的情况下运行该脚本。

答案1

[ -f /*.txt ]

仅当存在时才会返回 true(且只有一个)非隐藏文件,/其名称以 结尾.txt,并且该文件是常规文件或常规文件的符号链接。

这是因为通配符在传递给命令之前已由 shell 扩展(此处[)。

因此,如果有/a.txtand /b.txt[将传递 5 个参数:[-f/a.txt和。然后会抱怨给出了太多的参数。/b.txt][-f

如果您想检查该*.txt模式是否扩展到至少一个非隐藏文件(常规或非常规),请在bashshell 中执行以下操作:

shopt -s nullglob
set -- *.txt
if [ "$#" -gt 0 ]; then
  ./script "$@" # call script with that list of files.
fi
# Or with bash arrays so you can keep the arguments:
files=( *.txt )
# apply C-style boolean on member count
(( ${#files[@]} )) && ./script "${files[@]}"

shopt -s nullglobbash特定的(shopt是,nullglob实际上来自zsh),但是像ksh93, zsh, yash, 这样的 shelltcsh具有等效的语句。

zsh,测试为是否有与模式匹配的文件可以使用匿名函数和N(for nullglob) 和Y1(在第一次查找后停止) glob 限定符来编写:

if ()(($#)) *.txt(NY1); then
  do-something
fi

请注意,这些文件通过读取目录的内容来查找这些文件,它根本不会尝试访问这些文件,这使得它比调用诸如lsstat在 shell 计算的文件列表上的命令的解决方案更有效。

标准sh等效项是:

set -- [*].txt *.txt
case "$1$2" in
  ('[*].txt*.txt') ;;
  (*) shift; script "$@"
esac

问题是,对于 Bourne 或 POSIX shell,如果模式不匹配,它就会扩展到自身。因此,如果*.txt展开为*.txt,您不知道是因为.txt目录中没有文件,还是因为有一个名为 的文件*.txt。使用[*].txt *.txt允许区分两者。

现在,如果您想检查是否*.txt至少匹配一个常规的文件或符号链接到常规文件(就像您[ -f *.txt ]建议的那样),或者所有匹配的文件*.txt都是常规的文件(在符号链接解析之后),那是另一回事。

zsh

if ()(($#)) *.txt(NY1-.); then
  echo "there is at least one regular .txt file"
fi
if ()(($#)) *.txt(NY1^-.); then
  echo "there is at least one non-regular .txt files"
fi

-如果您想在符号链接解析之前进行测试,请删除 ,即将符号链接视为非常规文件,无论它们是否指向常规文件)。

答案2

一个可能的解决方案也是 Bash 内置compgen。该命令返回通配模式的所有可能匹配项,并具有指示是否有任何文件匹配的退出代码。

compgen -G "/*.text" > /dev/null && ./script

我在寻找更快的解决方案时发现了这个问题。

答案3

你总是可以使用find

find . -maxdepth 1 -type f -name "*.txt" 2>/dev/null | grep -q . && ./script

解释:

  • find .:搜索当前目录
  • -maxdepth 1: 不搜索子目录
  • -type f:仅搜索常规文件
  • name "*.txt":搜索以以下结尾的文件.txt
  • 2>/dev/null:将错误消息重定向到/dev/null
  • | grep -q .: grep 任意字符,如果没有找到字符则返回 false。
  • && ./script./script仅当上一个命令成功时才执行(&&

答案4

正如 Chazelas 指出的那样,如果通配符扩展匹配多个文件,您的脚本将会失败。

然而,我使用了一个技巧(即使我不太喜欢它)绕过:

PATTERN=(/*.txt)
if [ -f ${PATTERN[0]} ]; then
...
fi

怎么运行的?

通配符扩展将匹配文件名数组,如果有的话我们得到第一个,如果不匹配则返回 null。

相关内容