Bash
“查找:路徑必須在表達式之前:”僅在腳本中執行時出錯
如果我在命令行中執行 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 ';' )
),因此您仍然需要確保您可以控制其內容。