Check if a file exists with wildcard in shell script
我正在尝试检查文件是否存在,但使用通配符。下面是我的例子:
1 2 3 | if [ -f"xorg-x11-fonts*" ]; then printf"BLAH" fi |
我也试过没有双引号。
最简单的应该是依赖
1 2 3 4 5 | if ls /path/to/your/files* 1> /dev/null 2>&1; then echo"files do exist" else echo"files do not exist" fi |
我重新定向了
编辑:由于这个答案引起了人们的注意(以及非常有用的评论评论),这里有一个优化,它也依赖于全局扩展,但避免了使用
1 2 3 4 5 6 7 8 9 10 | 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看不到路径名参数,列出当前目录的内容并成功,这是错误的。gnu stat,如果没有参数或者参数命名一个不存在的文件,它总是会失败,这将更加健壮。此外,&;>重定向运算符是一种bashim。
1 2 3 4 5 6 | if stat --printf='' /path/to/your/files* 2>/dev/null then echo found else echo not found fi |
更好的是gnu find,它可以在内部处理通配符搜索,并在找到一个匹配的文件时立即退出,而不是浪费时间处理由shell扩展的可能巨大的文件列表;这还避免了shell可能溢出其命令行缓冲区的风险。
1 2 3 4 5 6 | if test -n"$(find /dir/to/search -maxdepth 1 -name 'files*' -print -quit)" then echo found else echo not found fi |
非GNU版本的find可能没有-maxdepth选项用于使find search仅为/dir/to/search,而不是根目录树的整个目录树。
这是我的答案-
1 2 3 4 5 6 | files=(xorg-x11-fonts*) if [ -e"${files[0]}" ]; then printf"BLAH" fi |
1 2 3 | for i in xorg-x11-fonts*; do if [ -f"$i" ]; then printf"BLAH"; fi done |
这将适用于多个文件和文件名中的空白。
您可以执行以下操作:
1 2 3 4 | set -- xorg-x11-fonts* if [ -f"$1" ]; then printf"BLAH" fi |
这适用于sh和派生词:ksh和bash。它不会创建任何子shell。$(…)和`…`命令创建一个子shell:它们分叉一个进程,而且效率低下。当然,它可以处理多个文件,而且这个解决方案可以是最快的,也可以是第二快的。
在没有火柴的情况下也能用。正如一位演讲人所说,不需要使用nullglob。$1将包含原始测试名称,因此测试-f$1不会成功,因为$1文件不存在。
更新:
好吧,现在我有了解决方案:
1 2 3 4 5 6 7 8 9 | files=$(ls xorg-x11-fonts* 2> /dev/null | wc -l) if ["$files" !="0" ] then echo"Exists" else echo"None found." fi > Exists |
也许这对某人有帮助:
1 2 3 | if ["`echo xorg-x11-fonts*`" !="xorg-x11-fonts*" ]; then printf"BLAH" fi |
这个问题并不是针对Linux/bash的,所以我想我应该添加PowerShell方法——它处理通配符的方式不同——您将它放在引号中,如下所示:
1 2 3 4 | 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 } |
我认为这是有帮助的,因为原始问题的概念一般包括"shell",而不仅仅是bash或linux,并且也适用于具有相同问题的PowerShell用户。
严格来说,如果你只想打印"blah",下面是解决方案:
1 | find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 'BLAH' -quit |
另一种方法是:
1 2 3 4 5 6 7 | doesFirstFileExist(){ test -e"$1" } if doesFirstFileExist xorg-x11-fonts* then printf"BLAH" fi |
但我认为最理想的方法是如下,因为它不会尝试对文件名进行排序:
1 2 3 | if [ -z `find . -maxdepth 1 -name 'xorg-x11-fonts*' -printf 1 -quit` ] then printf"BLAH" fi |
我使用的bash代码
1 2 3 | if ls /syslog/*.log > /dev/null 2>&1; then echo"Log files are present in /syslog/; fi |
谢谢!
这里有一个针对您的特定问题的解决方案,它不需要
1 2 3 | if ["$(echo xorg-x11-fonts*)" !="xorg-x11-fonts*" ]; then printf"BLAH" fi |
正如你所看到的,这只是比你所期望的要复杂一点,它依赖于这样一个事实:如果shell不能扩展glob,它就意味着不存在具有该glob的文件,并且
但是,如果我们要推广该过程,我们应该考虑到这样一个事实:文件的名称和/或路径中可能包含空格,并且glob char可以正确地扩展为Nothing(在您的示例中,该文件的名称正好是xorg-x11-fonts)。
这可以通过bash中的以下函数来实现。
1 2 3 4 5 | function doesAnyFileExist { local arg="$*" local files=($arg) [ ${#files[@]} -gt 1 ] || [ ${#files[@]} -eq 1 ] && [ -e"${files[0]}" ] } |
回到您的示例,可以这样调用它。
1 2 3 | if doesAnyFileExist"xorg-x11-fonts*"; then printf"BLAH" fi |
glob扩展应该发生在函数本身中,以便它正常工作,这就是为什么我将参数放在引号中的原因,这就是函数体中第一行的作用:这样,任何多个参数(可能是函数外部glob扩展的结果,也可能是伪参数)都可以合并为一个参数。另一种方法可能是,如果有多个参数,则会引发错误;另一种方法可能是忽略除第一个参数以外的所有参数。
函数体中的第二行将
函数体中的第三行有两个功能:
它首先检查数组中是否有多个元素。如果是这样的话,那就意味着地球肯定扩展到了某个地方(由于我们在第一行所做的),这反过来意味着至少存在一个与地球匹配的文件,这就是我们想要知道的。
如果在步骤1。我们发现数组中的元素少于2个,然后我们检查是否有一个元素,如果有,我们检查这个元素是否存在,通常的方法。我们需要做这个额外的检查,以说明没有glob字符的函数参数,在这种情况下,数组只包含一个未展开的元素。
我用这个:
1 2 3 4 | filescount=`ls xorg-x11-fonts* | awk 'END { print NR }'` if [ $filescount -gt 0 ]; then blah fi |
1 | if [ `ls path1/* path2/* 2> /dev/null | wc -l` -ne 0 ]; then echo ok; else echo no; fi |
imho最好在测试文件、全局或目录时始终使用
以下示例测试目录是否有条目:
1 2 3 4 | $ mkdir A $ touch A/b $ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . && echo 'not empty' not empty |
当
1 2 3 | $ rm A/b $ find A -maxdepth 0 -not -empty -print | head -n1 | grep -q . || echo 'empty' empty |
当
1 2 3 4 | $ 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 |
用任何其他
这种方法在shell脚本中很好地工作。最初的问题是寻找环球
1 2 3 4 5 6 | if find -maxdepth 0 -name 'xorg-x11-fonts*' -print | head -n1 | grep -q . then : the glob matched else : ...not fi |
注意,如果
找到了几个值得分享的解决方案。第一个问题仍然是"如果匹配太多,这将中断"问题:
1 | pat="yourpattern*" matches=($pat) ; [["$matches" !="$pat" ]] && echo"found" |
(回想一下,如果使用不带
如果脚本中有"shopt-s nullglob",您只需执行以下操作:
1 | matches=(yourpattern*) ; [["$matches" ]] && echo"found" |
现在,如果一个目录中可能有大量的文件,那么使用find非常困难:
1 | find /path/to/dir -maxdepth 1 -type f -name 'yourpattern*' | grep -q '.' && echo 'found' |
怎么样
1 2 3 4 5 6 | if ls -l | grep -q 'xorg-x11-fonts.*' # grep needs a regex, not a shell glob then # do something else # do something else fi |
如果使用通配符的网络文件夹上有大量文件,则存在问题(速度或命令行参数溢出)。
我的结局是:
1 2 3 | if [ -n"$(find somedir/that_may_not_exist_yet -maxdepth 1 -name \*.ext -print -quit)" ] ; then echo Such file exists fi |
您还可以剪切其他文件
1 2 3 | if [ -e $( echo $1 | cut -d"" -f1 ) ] ; then ... fi |
在ksh、bash和zsh shell中使用新的花式shmancy特性(本例不处理文件名中的空格):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # Declare a regular array (-A will declare an associative array. Kewl!) declare -a myarray=( /mydir/tmp*.txt ) array_length=${#myarray[@]} # Not found if the 1st element of the array is the unexpanded string # (ie, if it contains a"*") if [[ ${myarray[0]} =~ [*] ]] ; then echo"No files not found" elif [ $array_length -eq 1 ] ; then echo"File was found" else echo"Files were found" fi for myfile in ${myarray[@]} do echo"$myfile" done |
是的,这个闻起来像珍珠。很高兴我没有插手;)
试试这个
1 2 3 | fileTarget="xorg-x11-fonts*" filesFound=$(ls $fileTarget) # 2014-04-03 edit 2: removed dbl-qts around $(...) |
编辑2014-04-03(删除dbl引号并添加测试文件"charlie 22.html"(2个空格)
1 2 3 4 5 6 | case ${filesFound} in "" ) printf"NO files found for target=${fileTarget} " ;; * ) printf"FileTarget Files found=${filesFound} " ;; esac |
试验
1 2 3 4 5 6 7 8 9 10 11 12 | 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
人体试验
1 2 3 | if [ -e file ]; then ... fi |
适用于dirfile。
当做