使用 find 命令轉義問題
我需要找到目錄中的所有內容,不包括某些子目錄和文件。我的腳本需要將其作為函式呼叫:
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_args
和name_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" "$@" )
在
sh
shell 中,我們只有一個可用的數組,它是位置參數列表$@
,所以我們在其中收集我們的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
(通過轉義它們),但這不是您的函式執行的實際命令。