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

时间:2011-06-15 19:50:27

标签: shell sh wildcard

我正在尝试检查文件是否存在,但是使用通配符。这是我的例子:

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

我也尝试过没有双引号。

20 个答案:

答案 0 :(得分:365)

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

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

我重定向ls输出以使其完全无声。


编辑:由于这个答案得到了一些关注(非常有用的批评评论作为评论),这里的优化也依赖于全局扩展,但避免使用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的答案非常相似,但它避免了整个列表中不必要的迭代。

答案 1 :(得分:53)

如果你的shell有一个 nullglob 选项并且它已打开,那么将完全从命令行中删除不匹配任何文件的通配符模式。这将使 ls 看不到路径名参数,列出当前目录的内容并成功,这是错误的。 GNU stat ,如果没有参数或命名不存在的文件的参数,它总是会失败的。此外,&>重定向运算符是bashism.

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

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

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

非GNU版本的查找可能没有使用 -maxdepth 选项来查找仅搜索 / dir / to / search 而不是整个目录树。

答案 2 :(得分:36)

这是我的答案 -

files=(xorg-x11-fonts*)

if [ -e "${files[0]}" ];
then
    printf "BLAH"
fi

答案 3 :(得分:19)

for i in xorg-x11-fonts*; do
  if [ -f "$i" ]; then printf "BLAH"; fi
done

这将适用于多个文件,文件名中包含空格。

答案 4 :(得分:19)

您可以执行以下操作:

BOOL isAppFromAppStore;
      if ([NSData dataWithContentsOfURL:[NSBundle mainBundle].appStoreReceiptURL] != nil) {
            isAppFromAppStore = YES;
        } else {
            isAppFromAppStore = NO;
        }
    if (isAppFromAppStore) {
    NSLog(@"App is downloaded from AppStore");
    } else {
    NSLog(@"App is in Debug mode");
    }

这适用于sh和derivates:ksh和bash。它不会创建任何子shell。 $(..)和`...`命令创建一个子shell:它们分叉一个进程,它们效率低下。当然它适用于多个文件,这个解决方案可以是最快的,也可以是最快的解决方案。

当没有匹配时它也有效。没有必要使用nullglob作为其中一个comentatators说。 $ 1将包含原始测试名称,因此测试-f $ 1将不会成功,因为$ 1文件不存在。

答案 5 :(得分:14)

<强>更新

好的,现在我肯定有解决方案:

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

> Exists

答案 6 :(得分:13)

也许这会对某人有所帮助:

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

答案 7 :(得分:4)

严格地说,如果你只想打印“Blah”,这就是解决方案:

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

这是另一种方式:

doesFirstFileExist(){
    test -e "$1"
}

if doesFirstFileExist xorg-x11-fonts*
then printf "BLAH"
fi

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

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

答案 8 :(得分:3)

我使用的bash代码

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

谢谢!

答案 9 :(得分:2)

以下是您的特定问题的解决方案,该问题不需要for循环或外部命令,如lsfind等。

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

正如您所看到的,它只是比您希望的更复杂,并且依赖于以下事实:如果shell无法扩展glob,则意味着不存在具有该glob的文件并且{{1将按输出glob ,这允许我们仅进行字符串比较以检查是否存在任何这些文件。

如果我们要概括程序,我们应该考虑到文件在其名称和/或路径中可能包含空格这一事实char可以合理地扩展为空(在您的示例中,将是名称​​完全 xorg-x11-fonts的文件的情况)。

这可以通过 bash 中的以下功能实现。

echo

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

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

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

函数体中的第二行将if doesAnyFileExist "xorg-x11-fonts*"; then printf "BLAH" fi var设置为由扩展到的所有文件名构成的数组,每个数组元素对应一个。 如果文件名包含空格则没问题,每个数组元素都包含名称,包括空格。

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

  1. 首先检查数组中是否有多个元素。如果是这样,这意味着glob 肯定被扩展为某种东西(由于我们在第一行上做了什么),这反过来意味着至少有一个匹配glob的文件存在,这就是我们想要的要知道。

  2. 如果在步骤 1。,我们发现数组中的元素少于2个,那么我们检查是否有一个元素,如果是,我们检查是否存在,通常办法。我们需要进行额外的检查,以便考虑函数参数没有 glob chars,在这种情况下,数组只包含一个未展开元素。

    < / LI>

答案 10 :(得分:1)

我用这个:

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

答案 11 :(得分:1)

恕我直言,在测试文件,全局或目录时,最好始终使用find。执行此操作的绊脚石是find的退出状态:如果所有路径都已成功遍历,则为0,否则为> 0。传递给find的表达式在其退出代码中不会创建回显。

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

$ 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

-not -empty替换为任何其他find表达式,但是如果-exec打印到stdout的命令,请小心。在这种情况下,您可能希望grep寻找更具体的表达式。

这种方法在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分支。要区分案例使用$?

答案 12 :(得分:1)

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

答案 13 :(得分:0)

找到一些值得分享的简洁解决方案。第一个仍然受到“如果有太多匹配问题就会中断”的问题:

pat="yourpattern*" matches=($pat) ; [[ "$matches" != "$pat" ]] && echo "found"

(回想一下,如果使用不带[ ]语法的数组,则会得到数组的第一个元素。)

如果您的脚本中有“shopt -s nullglob”,则可以执行以下操作:

matches=(yourpattern*) ; [[ "$matches" ]] && echo "found"

现在,如果目录中可能有大量文件,那么使用find会非常困难:

find /path/to/dir -maxdepth 1 -type f -name 'yourpattern*' | grep -q '.' && echo 'found'

答案 14 :(得分:0)

在ksh,bash和zsh shell中使用新的花式shmancy功能(此示例不处理文件名中的空格):

# 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

是的,这确实闻起来像Perl。很高兴我没有介入其中;)

答案 15 :(得分:0)

您还可以删除其他文件

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

答案 16 :(得分:0)

如果使用通配符的网络文件夹中存在大量文件,则可疑(速度或命令行参数溢出)。

我最终得到了:

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

答案 17 :(得分:0)

怎么样

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

答案 18 :(得分:0)

试试这个

fileTarget="xorg-x11-fonts*"

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

编辑2014-04-03(删除了dbl-quotes并添加了测试文件'Charlie 22.html'(2个空格)

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

<强>测试

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包含您要检查的路径。

答案 19 :(得分:-15)

男子测试

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

适用于dir \ file。

问候