我正在尝试编写一条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。