Bash

為什麼使用雙括號“[[”檢查萬用字元匹配的文件是否存在失敗,而單括號“[”有效?

  • July 10, 2020

假設我生成了兩個目錄,每個目錄中都有文本文件,就像這樣

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

它什麼也不返回,表明所有文件確實在那裡。

為什麼在這種情況下,雙括號失敗,而單括號有效?我假設我應該始終使用雙括號,字元串匹配中的萬用字元是否會創建我不應該擁有的東西?

條件運算符測試是否存在具有給定名稱的-e文件。它不進行任何模式匹配。[[ -e fileclass1*1.txt ]]測試fileclass1*1.txt名稱中是否有一個帶有星號的文件,而不是是否至少有一個文件與萬用字元模式匹配fileclass1*1.txt

使用單括號[ ! -e $name ], 因為$name未加引號,所以 的值會name進行萬用字元替換和分詞。如果值為,nameDirectory1/fileclass1*1.txt效果取決於有多少文件與萬用字元模式匹配。

  • 如果該模式與任何文件都不匹配,則它保持未展開狀態。然後-e操作員看到文件名Directory1/fileclass1*1.txt,它不存在,所以[ ! -e $name ]是真的。
  • 如果模式匹配單個文件,則將其替換為該文件的名稱。然後-e操作員看到該文件名,它存在¹,所以[ ! -e $name ]是假的。
  • 如果模式匹配兩個或更多文件,則將其替換為匹配文件名列表,這會導致條件句中出現語法錯誤。在這種情況下,[ ! -e $name ]顯示錯誤消息並返回失敗狀態。

效果不同,[[ -e $name ]]因為[[ … ]]是特殊語法(而[普通命令的參數是正常擴展的)。內部沒有進行分詞或萬用字元擴展[[ … ]]

要解決您的實際問題,請參閱測試是否有與模式匹配的文件以執行腳本

¹除非在極少數情況下,由於競爭條件,文件在 shell 列出目錄內容以擴展萬用字元模式的時間和-e操作員檢查文件存在的時間之間被刪除。

引用自:https://unix.stackexchange.com/questions/597850