在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,不需要循环或外部命令(如ls
, find
等)。
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扩展到的所有文件名构成的数组 ,每个数组元素对应一个文件名。 如果文件名包含空格 , 那就好了 ,每个数组元素将包含名称(包括空格)。
函数体中的第三行做了两件事:
-
它首先检查数组中是否有多个元素。 如果是这样,这意味着glob 肯定会扩展到某些东西(由于我们在第一行做了什么),这又意味着至less有一个匹配glob的文件存在,这是我们想知道的。
-
如果在步骤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
用任何其他find
expression式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 \文件。
问候