Bash检查文件是否存在双括号测试和通配符

Wil*_*ett 8 bash

我正在编写一个Bash脚本,需要检查一个文件是否存在,看起来*.$1.*.ext我可以通过POSIX测试很容易地做到这一点,因为[ -f *.$1.*.ext ]返回true,但使用双括号[[ -f *.$1.*.ext ]]失败.

这只是为了满足好奇心,因为我无法相信扩展测试无法判断文件是否存在.我知道我可以使用,[[ `ls *.$1.*.ext` ]]但如果有多个匹配则匹配.我可能会把它管道wc或其他东西,但这似乎笨重.

是否有一种简单的方法可以使用双括号来检查是否存在使用通配符的文件?

编辑:我看到它[[ -f `ls -U *.$1.*.ext` ]]有效,但我仍然不想打电话给ls.

mkl*_*nt0 10

[ -f ... ]没有[[ -f ... ]](也没有其他文件测试操作符)被设计为使用模式(aka globs,通配符表达式) - 它们总是将它们的操作数解释为文字文件名.[1]

测试模式(glob)是否与一个文件完全匹配的一个简单技巧是使用辅助函数:

existsExactlyOne() { [[ $# -eq 1 && -f $1 ]]; }

if existsExactlyOne *."$1".*.ext; then # ....
Run Code Online (Sandbox Code Playgroud)

如果您只是对是否有任何匹配感兴趣- 即一个或多个 - 该功能甚至更简单:

exists() { [[ -f $1 ]]; }
Run Code Online (Sandbox Code Playgroud)

如果你想避免一个函数,它会变得更棘手:

警告:例如,此解决方案不区分常规文件目录(尽管可以修复).

if [[ $(shopt -s nullglob; set -- *."$1".*.ext; echo $#) -eq 1 ]]; then # ...
Run Code Online (Sandbox Code Playgroud)
  • 命令substitution($(...))中的代码执行以下操作:
    • shopt -s nullglob如果没有匹配项,则指示bash将模式扩展为字符串
    • set -- ...分配模式膨胀为位置参数的结果($1,$2其中命令替换运行子外壳的,...).
    • echo $# 简单地回应位置参数的计数,然后对应于匹配文件的计数;
  • 回显的数字(命令替换的标准输出)成为-eq操作符的左侧,(数字上)将其与之比较1.

同样,如果您只是对是否有任何匹配感兴趣- 即一个或多个 - 只需替换-eq-ge.


[1]
正如@Etan Reisinger在评论中指出的那样,在[ ... ](单括号语法)的情况下,shell会在操作员看到它之前扩展模式-f(正常的命令行解析规则适用).

相比之下,不同的规则适用于bash [[ ... ]],它被解析得不同,在这种情况下,简单地将模式视为文字(即,不扩展它).

无论哪种方式,它都不会(强大且可预测)与模式一起工作:

  • 随着[[ ... ]]永远不会起作用:文件测试运算符始终将模式视为文字.
  • 有了[ ... ]只是正常工作,如果有恰好是只有一个匹配.
    • 如果没有匹配:
      • 文件测试操作符将模式视为文字,如果nullglob为OFF(默认值),或者,如果nullglob为ON,则条件总是返回true,因为它被减少为-f,由于缺少操作数,不再被解释作为文件测试,但作为非空字符串(非空字符串计算结果为true)).
    • 如果存在MULTIPLE匹配:[ ... ]命令作为一个整体断开,因为模式然后扩展为多个单词,而文件测试运算符只接受一个参数.

  • @WilliamEverett:`*."$ 1".*.\ text`是一个_glob_,可能扩展为_multiple_参数 - 正确分区为单个文件名,即使是嵌入的空格(由于双引用"$ 1"部分).所以,如果只有一个匹配,`$ #`将在函数内部为1 - 无论1匹配是否嵌入了空格. (2认同)