Bash

使用 find 命令轉義問題

  • September 12, 2018

我需要找到目錄中的所有內容,不包括某些子目錄和文件。我的腳本需要將其作為函式呼叫:

function findStuff() {
 # define exclusions
 ignore_dirs=("$1" "*foo*")                        # exclude base dir
 ignore_files=("one.txt" "two.txt" "*three*.txt")
 # build patterns for find command
 dir_pattern=""
 file_pattern=""
 for i in "${ignore_dirs[@]}"; do dir_pattern=$dir_pattern" ! -path \"$i\""; done
 for i in "${ignore_files[@]}"; do file_pattern=$file_pattern" ! -name \"$i\""; done
 # find
 find "$1 $dir_pattern $file_pattern"
 # now do other stuff with the results...
}

findStuff /some/base/dir

但這給了我一個No such file or directory錯誤。

所以我想看看這個命令實際上是什麼,並嘗試echo find "$1 $dir_pattern $file_pattern"將它粘貼在命令行上並且它有效。然後我將它粘貼到腳本中並執行它,它也有效!

所以我認為它失敗是因為一些逃避問題。我做錯了什麼?

find將使用它獲得的第一個參數(直到第一個以-or開頭的參數!or ()作為頂級搜尋路徑。當您在函式中呼叫它時,您將給出find一個參數,即字元串$1 $dir_pattern $file_pattern(擴展變數)。未找到此路徑。

您還可以在您打算給予的參數中包含文字雙引號find。使用雙引號是為了防止 shell 擴展 glob 模式和拆分空格(或IFS變數包含的任何內容),但如果您使案例如! -name \"thing\",則雙引號將成為find用於與文件名進行比較的模式的一部分。

使用數組,並正確引用單獨的參數find

myfind () {
 local ignore_paths=( "$1" "*foo*" )
 local ignore_names=( "one.txt" "two.txt" "*three*.txt" )

 local path_args=()
 for string in "${ignore_paths[@]}"; do
     path_args+=( ! -path "$string" )
 done

 local name_args=()
 for string in "${ignore_names[@]}"; do
     name_args+=( ! -name "$string" )
 done

 find "$1" "${path_args[@]}" "${name_args[@]}"
}

每次我們追加到path_argsname_args上面時,我們將三個元素添加到列表中,!-path-name, 和"$string"。展開"${path_args[@]}"and時"${name_args[@]}"(注意雙引號),元素將被單獨引用。


等效實現適用於/bin/sh

myfind () (
   topdir=$1

   set --

   # paths to ignore
   for string in "$topdir" "*foo*"; do
       set -- "$@" ! -path "$string"
   done

   # names to ignore
   for string in "one.txt" "two.txt" "*three*.txt"; do
       set -- "$@" ! -name "$string"
   done

   find "$topdir" "$@"
)

shshell 中,我們只有一個可用的數組,它是位置參數列表$@,所以我們在其中收集我們的find選項。bash顯然,也可以編寫特定的解決方案以使用單個數組,並且sh變體也會執行bash


最後,您的echo測試輸出並不能準確表示您的函式將執行的命令。

考慮一下:

cat "my file name"

它執行cat在一個叫做 的東西上my file name,並且

echo cat "my file name"

輸出字元串cat my file name。這是因為 shell 在執行命令之前會刪除字元串周圍的引號。執行命令,cat將查找三個文件,而不是一個。

當您將命令複製粘貼到 shell 中時,您的命令執行良好,因為您在輸出的字元串中包含了文字雙引號echo(通過轉義它們),但這不是您的函式執行的實際命令。

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