在shell脚本中检查文件是否存在通配符

我试图检查一个文件是否存在,但有一个通配符。 这是我的例子:

if [ -f "xorg-x11-fonts*" ]; then printf "BLAH" fi 

我也尝试过没有双引号。

最简单的应该是依靠ls返回值(当文件不存在时它会返回非零值):

 if ls /path/to/your/files* 1> /dev/null 2>&1; then echo "files do exist" else echo "files do not exist" fi 

我redirectls输出,使其完全沉默。


编辑:由于这个答案已经得到了一些关注(和评论非常有用的批评评论),这里是一个优化,也依赖glob扩展,但避免使用ls

 for f in /path/to/your/files*; do ## Check if the glob gets expanded to existing files. ## If not, f here will be exactly the pattern above ## and the exists test will evaluate to false. [ -e "$f" ] && echo "files do exist" || echo "files do not exist" ## This is all we needed to know, so we can break after the first iteration break done 

这与@ grok12的答案非常相似,但它避免了整个列表中不必要的迭代。

如果你的shell有一个nullglob选项并且它被打开,那么一个不匹配文件的通配符模式将被从命令行中删除。 这将使ls看不到path名参数,列出当前目录的内容并成功,这是错误的。 GNU stat ,如果没有给定参数或命名一个不存在文件的参数,它总是会失败,这样会更健壮。 另外,&>redirect操作符是一个双击操作。

 if stat --printf='' /path/to/your/files* 2>/dev/null then echo found else echo not found fi 

更好的是GNU find ,它可以在内部处理通配符search,并在find一个匹配的文件时立即退出,而不是浪费时间处理由shell扩展的可能庞大的列表。 这也避免了shell可能溢出其命令行缓冲区的风险。

 if test -n "$(find /dir/to/search -maxdepth 1 -name 'files*' -print -quit)" then echo found else echo not found fi 

find的非GNU版本可能没有这里使用的-maxdepth选项来查找只search/ dir / to / search,而不是整个目录树。

这是我的答案 –

 files=(xorg-x11-fonts*) if [ -e "${files[0]}" ]; then printf "BLAH" fi 
 for i in xorg-x11-fonts*; do if [ -f "$i" ]; then printf "BLAH"; fi done 

这将使用多个文件和文件名中的空格。

更新:

好吧,现在我肯定有解决办法:

 files=$(ls xorg-x11-fonts* 2> /dev/null | wc -l) if [ "$files" != "0" ] then echo "Exists" else echo "None found." fi > Exists 

也许这会帮助别人:

 if [ "`echo xorg-x11-fonts*`" != "xorg-x11-fonts*" ]; then printf "BLAH" fi 

您可以执行以下操作:

 set -- xorg-x11-fonts* if [ -f "$1" ]; then printf "BLAH" fi 

这适用于sh和派生:ksh和bash。 它不创build任何子shell。 $(..)和`…`命令创build一个子shell:他们分叉进程,效率低下。

这个问题不是特定于Linux / Bash,所以我想添加Powershell的方式 – 对待通配符不同 – 你把它放在如下所示的引号中:

 If (Test-Path "./output/test-pdf-docx/Text-Book-Part-I*"){ Remove-Item -force -v -path ./output/test-pdf-docx/*.pdf Remove-Item -force -v -path ./output/test-pdf-docx/*.docx } 

我认为这很有帮助,因为原始问题的概念通常不仅包括Bash或Linux,而且也包含相同问题的Powershell用户。

我使用的bash代码

 if ls /syslog/*.log > /dev/null 2>&1; then echo "Log files are present in /syslog/; fi 

谢谢!

严格来说,如果你只想打印“Blah”,这里的解决scheme是:

 find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 'BLAH' -quit 

这是另一种方式:

 doesFirstFileExist(){ test -e "$1" } if doesFirstFileExist xorg-x11-fonts* then printf "BLAH" fi 

但我认为最优化的是如下,因为它不会尝试对文件名进行sorting:

 if [ -z `find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 1 -quit` ] then printf "BLAH" fi 

这是针对您的特定问题的解决scheme,不需要循环或外部命令(如lsfind等)。

 if [ "$(echo xorg-x11-fonts*)" != "xorg-x11-fonts*" ]; then printf "BLAH" fi 

正如你所看到的,它比你想要的要复杂得多,而且依赖于如果shell不能扩展glob,那就意味着没有那个glob的文件存在,并且echo会输出glob ,它允许我们做一个纯粹的string比较来检查是否有任何这些文件存在。

但是,如果我们要推广这个过程 ,我们应该考虑到这样一个事实,即文件可能在其名称和/或path中包含空格 ,并且glob char可以正确地展开为空(在你的例子中,这将是一个名字正好是 xorg-x11-fonts的文件)​​。

这可以通过以下函数在bash中实现

 function doesAnyFileExist { local arg="$*" local files=($arg) [ ${#files[@]} -gt 1 ] || [ ${#files[@]} -eq 1 ] && [ -e "${files[0]}" ] } 

回到你的例子,可以像这样调用它。

 if doesAnyFileExist "xorg-x11-fonts*"; then printf "BLAH" fi 

Glob扩展应该在函数本身内部发生,以使其正常工作,这就是为什么我把引数放在引号中,这就是函数体中的第一行:因此任何多个参数(可能是glob的结果函数外的扩展以及伪参数)将被合并为一个。 如果有多个参数,另一种方法可能是提出错误,另一种方法是忽略除第一个参数之外的所有错误。

函数体中的第二行将files var设置为由glob扩展到的所有文件名构成的数组 ,每个数组元素对应一个文件名。 如果文件名包含空格那就好了 ,每个数组元素将包含名称包括空格)。

函数体中的第三行做了两件事:

  1. 它首先检查数组中是否有多个元素。 如果是这样,这意味着glob 肯定会扩展到某些东西(由于我们在第一行做了什么),这又意味着至less有一个匹配glob的文件存在,这是我们想知道的。

  2. 如果在步骤1,我们发现我们得到的数组less于2个,那么我们检查是否有一个,如果是的话,我们检查是否存在,通常的方式。 我们需要进行额外的检查,以解释没有 glob字符的函数参数,在这种情况下,数组只包含一个未展开的元素。

我使用这个:

 filescount=`ls xorg-x11-fonts* | awk 'END { print NR }'` if [ $filescount -gt 0 ]; then blah fi 

恕我直言,最好总是在testing文件,球体或目录时使用find 。 这样做的绊脚石是find的退出状态:0,如果所有的path都成功遍历,否则> 0。 您传递给find的expression式在其退出代码中不会创build回显。

以下示例testing目录是否包含条目:

 $ mkdir A $ touch A/b $ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo 'not empty' not empty 

A没有文件时grep失败:

 $ rm A/b $ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . || echo 'empty' empty 

A不存在时, grep再次失败,因为find只打印到stderr:

 $ rmdir A $ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo 'not empty' || echo 'empty' find: 'A': No such file or directory empty 

用任何其他findexpression式replace-not -empty ,但要小心,如果你执行打印到标准输出的命令。 在这种情况下,您可能希望获得更具体的expression。

这种方法在shell脚本中很好地工作。 最初的问题是寻找glob xorg-x11-fonts*

 if find -maxdepth 0 -name 'xorg-x11-fonts*' -print | head -n1 | grep -q . then : the glob matched else : ...not fi 

请注意,如果xorg-x11-fonts*不匹配,或者find遇到错误,则会到达else分支。 区分使用$?的情况$?

 if [ `ls path1/* path2/* 2> /dev/null | wc -l` -ne 0 ]; then echo ok; else echo no; fi 

尝试这个

 fileTarget="xorg-x11-fonts*" filesFound=$(ls $fileTarget) # 2014-04-03 edit 2: removed dbl-qts around $(...) 

编辑2014-04-03(删除dbl引号并添加了testing文件“Charlie 22.html”(2个空格)

 case ${filesFound} in "" ) printf "NO files found for target=${fileTarget}\n" ;; * ) printf "FileTarget Files found=${filesFound}\n" ;; esac 

testing

 fileTarget="*.html" # where I have some html docs in the current dir FileTarget Files found=Baby21.html baby22.html charlie 22.html charlie21.html charlie22.html charlie23.html fileTarget="xorg-x11-fonts*" NO files found for target=xorg-x11-fonts* 

请注意,这仅适用于当前目录,或者var fileTarget包含您要检查的path。

如果使用通配符的networking文件夹上有大量文件是有问题的(速度或命令行参数溢出)。

我结束了:

 if [ -n "$(find somedir/that_may_not_exist_yet -maxdepth 1 -name \*.ext -print -quit)" ] ; then echo Such file exists fi 

你也可以剪掉其他文件

 if [ -e $( echo $1 | cut -d" " -f1 ) ] ; then ... fi 

怎么样

 if ls -l | grep -q 'xorg-x11-fonts.*' # grep needs a regex, not a shell glob then # do something else # do something else fi 

人testing

 if [ -e file ]; then ... fi 

将工作dir \文件。

问候