在 Bash if 条件中,如何检查是否存在与简单通配符表达式匹配的文件?

在 Bash if 条件中,如何检查是否存在与简单通配符表达式匹配的文件?

愚蠢的是,我一直在脚本中使用这样的条件:

if [ $(ls FOO* 2> /dev/null) ] # if files named "FOO*" were downloaded 
then
    echo "Files found" 
    # ... process and email results
else
    echo "Not found"
    # ... email warning that no files were found (against expectations)
fi

这适用于零和一文件命名FOO*,但是如果有多个则失败。从日志中我发现了几个不同的错误消息:

[: FOO_20131107_082920: unary operator expected
[: FOO_20131108_070203: binary operator expected
[: too many arguments

我的问题是:在 Bash 条件下检查if是否正确的方法是什么一个或多个名称开头的文件FOO存在吗?

GNU bash,版本 4.2.25(1)-release (x86_64-pc-linux-gnu)

答案1

发生这种情况是因为您对 ls 的命令替换输出空格,并且它最终在传递给 之前经历了分词[。一种不易破坏的方法是将文件放入数组中,然后检查该数组是否至少有一个成员。

shopt -s nullglob

files=( FOO* )
if (( ${#files[@]} )); then
    # there were files
fi

这是有效的,因为((默认情况下,如果值不等于 0,则返回 true,并${#files[@]}获取数组中的项目数(如果存在与 glob 匹配的文件,则该数将 >0)。

只要nullglob未设置,您也可以执行类似的操作:

if ls FOO* >/dev/null 2>&1; then
    # there were files
fi

这只是检查 的退出代码ls,如果您传递了一个不存在的文件名(文字FOO*,如果没有匹配的文件名,则该退出代码将为 1 (当然,除非您是邪恶的并且有一个名为 的文件FOO*,在这种情况下它将返回 0 :-) ))。

请注意,这两者也匹配目录。如果您确实只想匹配常规文件,则需要测试:

for file in FOO*; do
    if [[ -f $file ]]; then
        # file found, do some stuff and break
        break
    fi
done

答案2

上面的多行答案不符合我对单行解决方案且不修改环境的愿望。这是一条可能适合您的通用单行:

echo $(ls FOO* 2>/dev/null | wc -w)

/dev/null 是因为ls如果没有文件就会抛出错误。这只是忽略 ls 并根据“单词”(实际上是文件名)的数量来计算找到的文件数量。您可以在简单的 if 语句中使用它:

if [ 0 -lt $(ls FOO* 2>/dev/null | wc -w) ]; then
 echo "Some FOO is there."
fi

这种方法具有灵活性,不仅可以检查是否存在任何匹配的文件,还可以选择检查是否存在多少匹配的文件存在(只需更改0和/或-lt),然后您在此基础上进一步进行。

我还没有检查其中包含通配符或空格的文件/文件夹;我怀疑上述方法会给出错误的空格计数,特别是,如果需要超过 0 的精确计数,这只是一个问题。我为此尝试的一个修复方法是使用$(ls -l FOO* 2>/dev/null | wc -l),它将按行分解列表,因此应该是更容易接受空白。

答案3

还有另一种选择,使用案件,

FILES=FOO*
FILES=$(echo $FILES)
case $FILES in FOO\*) echo "Not found" ;; esac

这是因为 case 只需要测试一个参数。

相关内容