为什么使用双括号“[[”检查通配符匹配的文件是否存在会失败,而使用单括号“[”可以?

为什么使用双括号“[[”检查通配符匹配的文件是否存在会失败,而使用单括号“[”可以?

假设我生成了两个目录,每个目录中都有文本文件,如下所示

mkdir "Directory1"
mkdir "Directory2"
touch "Directory1/fileclass1_"{1..5}".txt"
touch "Directory1/fileclass2_"{1..5}".txt"
touch "Directory2/fileclass1_"{1..5}".txt"
touch "Directory2/fileclass2_"{1..5}".txt"

假设我通过执行以下操作来验证所有文件都在里面

A=( "Directory1" "Directory2" )
B=( "fileclass1" "fileclass2" )
for a in "${A[@]}"; do
    for b in "${B[@]}"; do
        for i in {1..5}; do
            name="${a}/${b}*${i}.txt"
            [[ ! -e $name ]] && echo "$name Does Not Exist"
        done
    done
done

这返回

Directory1/fileclass1*1.txt Does Not Exist
Directory1/fileclass1*2.txt Does Not Exist
...

但是,如果我用单括号替换双括号,我得到

A=( "Directory1" "Directory2" )
B=( "fileclass1" "fileclass2" )
for a in "${A[@]}"; do
    for b in "${B[@]}"; do
        for i in {1..5}; do
            name="${a}/${b}*${i}.txt"
            [ ! -e $name ] && echo "$name Does Not Exist"
        done
    done
done

它什么也不返回,表明所有文件确实都在那里。

为什么在这种情况下,双括号失败,而单括号有效?我假设我应该始终使用双括号,字符串匹配中的通配符是否创建了我不应该拥有的东西?

答案1

条件-e运算符测试给定名称的文件是否存在。它不进行任何模式匹配。[[ -e fileclass1*1.txt ]]测试是否存在fileclass1*1.txt名称中带有星号的文件,而不是测试是否至少有一个与通配符模式匹配的文件fileclass1*1.txt

对于单括号[ ! -e $name ],由于$name未加引号,因此 的值name会经历通配符替换和分词。如果name的值为Directory1/fileclass1*1.txt,则效果取决于有多少个文件与通配符模式匹配。

  • 如果该模式与任何文件都不匹配,则它不会展开。然后-e操作员看到文件名Directory1/fileclass1*1.txt不存在,所以[ ! -e $name ]是true。
  • 如果该模式与单个文件匹配,它将被该文件的名称替换。然后-e操作员看到该文件名存在,因此[ ! -e $name ]为 false。
  • 如果模式匹配两个或多个文件,它将被匹配文件名列表替换,这会导致条件中出现语法错误。在这种情况下,[ ! -e $name ]显示错误消息并返回失败状态。

效果不同,[[ -e $name ]]因为[[ … ]]是特殊语法(而[是参数正常扩展的普通命令)。内部没有进行分词或通配符扩展[[ … ]]

要解决您的实际问题,请参阅测试是否存在与模式匹配的文件以执行脚本

1除非在不太可能的情况下,由于竞争条件,否则在 shell 列出目录内容以扩展通配符模式的时间和操作员检查文件是否存在的时间之间,文件将被删除。-e

相关内容