Bash

“查找:路徑必須在表達式之前:”僅在腳本中執行時出錯

  • March 4, 2021

如果我在命令行中執行 find 命令,它可以正常工作,但如果我在腳本中嘗試它,我會收到此錯誤。我像這樣執行命令

FILTER=" | grep -v \"test\""
files=`find . -type f -regex \".*$ext\" $FILTER`

如果我做

echo "find . -type f -regex \".*$ext\" $FILTER" 

它輸出

find . -type f -regex ".*.cpp" | grep -v "test"

這在命令行上工作正常。我怎樣才能讓它在腳本中工作?我也嘗試轉義 *,但得到相同的錯誤。

我也注意到了

find . -type f -regex \".*$ext\"

在我的 shell 腳本中執行時沒有輸出,我不知道為什麼,因為就像上面一樣,如果我在命令行執行它,我會得到一個 .cpp 文件的列表。

當 shell 到達“擴展”階段時,控制操作符(例如|)已經被辨識出來。擴展的結果不會在搜尋控制結構時再次解析。

當命令替換在

files=`find . -type f -regex \".*$ext\" $FILTER`

被擴展後,Bash 將其解析為一個簡單的命令 ( find),後跟幾個參數,其中兩個需要擴展。您可以打開跟踪以查看實際的擴展命令:

$ set -x
$ find . -type f -regex \".*$ext\" $FILTER
+ find . -type f -regex '".*cpp"' '|' grep -v '"test"'

如果你把它與

$ set -x
$ find . -type f -regex ".*.cpp" | grep -v "test"
+ grep --color=auto -v test
+ find . -type f -regex '.*.cpp'

您可以清楚地看到,在第一種情況下,|被用作find.

要動態建構和執行命令字元串,您需要顯式添加新的解析階段。eval是一種方法:

$ set -x
$ files=$(eval "find . -type f -regex \".*$ext\" $FILTER")
++ eval 'find . -type f -regex ".*cpp"  | grep -v "test"'
+++ find . -type f -regex '.*cpp'
+++ grep --color=auto -v test

但請注意,在將變數作為腳本執行時,出於明顯的安全原因,確保您可以控制變數的內容非常重要。由於eval往往還會使程序更難閱讀和調試,因此建議僅將其用作最後的手段。

在您的情況下,更好的方法可能是:

filter=( -regex ".*$ext" '!' -name "*test*" )
find . -type f "${filter[@]}" -exec bash -c '
 # The part of your script that works with "files" goes here
 # "$@" holds a batch of file names
 ' mybash {} +

它利用了find’ 的靈活性並正確處理包含換行符的文件名 -find通常,除非您使用類似mapfile -d '' files < <(find ... -print0)(假設 Bash(自 4.4 版起)和find支持非標準的實現-print0)。您可以在為什麼循環查找 find 的輸出不好?,也與管道find的輸出有關。

再次注意,filter數組的元素可能會導致任意程式碼的執行(想想看filter=( -exec something_evil ';' )),因此您仍然需要確保您可以控制其內容。

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