我正在尝试编写一条if
语句来测试是否有任何文件与特定模式匹配。如果目录中有文本文件,它应该运行给定的脚本。
我目前的代码:
if [ -f /*.txt ]; then ./script fi
请给一些想法;我只想.txt
在目录中有 a 的情况下运行该脚本。
答案1
[ -f /*.txt ]
仅当存在时才会返回 true一(且只有一个)非隐藏文件,/
其名称以 结尾.txt
,并且该文件是常规文件或常规文件的符号链接。
这是因为通配符在传递给命令之前已由 shell 扩展(此处[
)。
因此,如果有/a.txt
and /b.txt
,[
将传递 5 个参数:[
、-f
、/a.txt
和。然后会抱怨给出了太多的参数。/b.txt
]
[
-f
如果您想检查该*.txt
模式是否扩展到至少一个非隐藏文件(常规或非常规),请在bash
shell 中执行以下操作:
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 nullglob
是bash
特定的(shopt
是,nullglob
实际上来自zsh
),但是像ksh93
, zsh
, yash
, 这样的 shelltcsh
具有等效的语句。
与zsh
,测试为是否有与模式匹配的文件可以使用匿名函数和N
(for nullglob
) 和Y1
(在第一次查找后停止) glob 限定符来编写:
if ()(($#)) *.txt(NY1); then
do-something
fi
请注意,这些文件通过读取目录的内容来查找这些文件,它根本不会尝试访问这些文件,这使得它比调用诸如ls
或stat
在 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
答案4
正如 Chazelas 指出的那样,如果通配符扩展匹配多个文件,您的脚本将会失败。
然而,我使用了一个技巧(即使我不太喜欢它)绕过:
PATTERN=(/*.txt)
if [ -f ${PATTERN[0]} ]; then
...
fi
怎么运行的?
通配符扩展将匹配文件名数组,如果有的话我们得到第一个,如果不匹配则返回 null。