我正在尝试编写一条if
语句来测试是否有任何文件符合特定模式。如果目录中有文本文件,则应运行给定脚本。
我的代码当前:
if [ -f /*.txt ]; then ./script fi
请给点想法;我只想.txt
在目录中有一个脚本时运行该脚本。
find
按照stackoverflow上的说明使用。
我正在尝试编写一条if
语句来测试是否有任何文件符合特定模式。如果目录中有文本文件,则应运行给定脚本。
我的代码当前:
if [ -f /*.txt ]; then ./script fi
请给点想法;我只想.txt
在目录中有一个脚本时运行该脚本。
find
按照stackoverflow上的说明使用。
Answers:
[ -f /*.txt ]
只有在有 一个(只有一个)非隐藏文件/
结尾.txt
且该文件是常规文件或指向常规文件的符号链接时,。
这是因为通配符在传递给命令(在此处[
)之前已被外壳扩展。
所以,如果有一个/a.txt
和/b.txt
,[
将通过5个参数:[
,-f
,/a.txt
,/b.txt
和]
。[
然后会抱怨-f
给出了太多的争论。
如果您要检查 *.txt
模式是否扩展到至少一个非隐藏文件(常规文件或非常规文件):
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
具体的,但炮弹一样ksh93
,zsh
,yash
,tcsh
具有等效的语句。
请注意,它通过读取目录内容来查找那些文件,它根本不会尝试访问这些文件,这使其比调用诸如 ls
或stat
由shell计算文件的名单上。
标准 sh
等效项为:
set -- [*].txt *.txt
case "$1$2" in
('[*].txt*.txt') ;;
(*) shift; script "$@"
esac
问题在于,对于Bourne或POSIX壳,如果模式不匹配,它将扩展为自身。因此,如果*.txt
扩展到*.txt
,则不知道是由于.txt
目录中没有文件还是因为存在一个名为的文件*.txt
。使用[*].txt *.txt
允许在两者之间进行区分。
[ -f /*.txt ]
与相比,速度相当快compgen
。
[ -f /*.txt ]
可能是错误的,但是在我对包含3425
文件的目录的测试中,该文件94
的速度是非隐藏的txt文件,compgen -G "*.txt" > /dev/null 2>&1
其速度最快set -- *.txt; [ "$#" -gt 0 ]
(在我的情况下,如果重复10000次,两者都为20.5秒)。
您可以随时使用find
:
find . -maxdepth 1 -type f -name "*.txt" 2>/dev/null | grep -q . && ./script
说明:
find
仅当找不到文件时返回false,否则找不到任何文件则返回false。您想通过管道将输出grep -q .
检查到是否找到了东西。
chmod a-x .
。
内置的Bash也是可能的解决方案compgen
。该命令返回一个globbing模式的所有可能的匹配,并具有一个退出代码,指示是否有任何文件匹配。
compgen -G "/*.text" > /dev/null && ./script
我在寻找更快的解决方案时发现了这个问题。
LC_ALL=C compgen -G "*.txt" > /dev/null
。
这是一个班轮:
$ ls
file1.pl file2.pl
$ stat -t *.pl >/dev/null 2>&1 && echo "file exists" || echo "file doesn't exist"
file exists
$ stat -t -- *.txt >/dev/null 2>&1 && echo "file exists" || echo "file don't exist"
file don't exist
此方法利用||
和&&
在bash中运算符。这些是“或”和“与”运算符。
因此,如果stat命令返回的值$?
等于0,则echo
调用第一个命令;如果返回的是1,则调用第二个命令echo
调用。
# a failure
$ stat -t -- *.txt >/dev/null 2>&1
$ echo "$?"
1
# a success
$ stat -t -- *.pl >/dev/null 2>&1
$ echo "$?"
0
这个问题已在stackoverflow上广泛讨论:
stat
时ls -d
能做到同样?
ls -d
列出目录?例如,当我尝试列出其中包含文件的目录时,似乎没有用ls -d *.pl
。
&&
通过ls *.txt
,它也能发挥作用。确保/dev/null
按照@slm的建议将stdout和stderr发送到。
ls *.txt
并且目录中没有文件,则将返回$? = 2
,这将如果仍然工作的话,但是这是我的原因选择一个stat
以上ls
。我想要0表示成功,而1表示失败。
ls -d
是列出目录而不是目录。所以ls -d
只是做了lstat
该文件,就像GNU stat
一样。故障时非零退出状态命令返回的内容是系统特定的,因此对其进行假设几乎没有意义。
正如Chazelas指出的,如果通配符扩展匹配多个文件,则脚本将失败。
但是,我有一个窍门(即使我不太喜欢)也可以解决:
PATTERN=(/*.txt)
if [ -f ${PATTERN[0]} ]; then
...
fi
怎么运行的?
通配符扩展将匹配文件名数组,如果有文件名,则获取第一个文件名;如果不匹配,则返回null。
.txt
一些类型为regular的文件。请在之后尝试mkdir a.txt; mkfifo b.txt; echo regular > c.txt
。
简单为:
cnt=`ls \*.txt 2>/dev/null | wc -l`
if [ "$cnt" != "0" ]; then ./script fi
wc -l
计算扩展通配符中的行数。
我喜欢以前的数组解决方案,但是这可能会浪费大量文件-外壳程序将使用大量内存来构建数组,并且只有第一个元素会被测试。
这是我昨天测试的替代结构:
$ cd /etc; if [[ $(echo * | grep passwd) ]];then echo yes;else echo no;fi
yes
$ cd /etc; if [[ $(echo * | grep password) ]];then echo yes;else echo no;fi
no
grep的退出值似乎在确定通过控制结构的路径。这也将使用正则表达式而不是外壳模式进行测试。我的某些系统具有“ pcregrep”命令,该命令允许进行更复杂的正则表达式匹配。
(在阅读了有关解析它的上述批评之后,我确实编辑了此答案以删除命令替换中的“ ls”。)
/
吗?另外,您之前还缺少分号fi
。