
我正在嘗試使用 搜尋文件find
,並將這些文件放入 Bash 數組中,以便我可以對它們(例如ls
或grep
它們)執行其他操作。但我不明白為什麼readarray
不讀取find
通過管道輸入的輸出。
假設我當前目錄中有兩個文件,file1.txt
並且file2.txt
.所以find
輸出如下:
$ find . -name "file*"
./file1.txt
./file2.txt
所以我想將其通過管道傳輸到一個數組中,該數組的兩個元素是字串"./file1.txt"
和"./file2.txt"
(顯然不帶引號)。
我已經嘗試過這個,還有其他一些事情:
$ declare -a FILES
$ find . -name "file*" | readarray FILES
$ echo "${FILES[@]}"; echo "${#FILES[@]}"
0
正如您從輸出中看到的echo
,我的陣列是空的。
那我到底做錯了什麼?為什麼readarray
不讀取find
的輸出作為其標準輸入並將這些字串放入數組中?
答案1
使用管道時,bash 在子 shell 中執行命令。因此,該數組已填充,但位於子 shell 中,因此父 shell 無法存取它。您可能還需要該-t
選項,以便不將該行分隔符號儲存在陣列成員中,因為它們不是檔案名稱的一部分。
使用進程替換:
readarray -t FILES < <(find .)
請注意,它不適用於路徑中包含換行符號的檔案。除非您能保證不會出現這種情況,否則您需要使用 NUL 分隔記錄而不是換行符號分隔記錄:
readarray -td '' < <(find . -print0)
(該-d
選項是在 bash 4.4 中新增的)
¹ 使用該選項時,最後一個管道組件除外lastpipe
,但這僅適用於bash
.
答案2
正確的解決方案是:
unset a; declare -a a
while IFS= read -r -u3 -d $'\0' file; do
a+=( "$file" ) # or however you want to process each file
done 3< <(find /tmp -type f -print0)
這類似於什麼格雷格的 Bash常見問題 020詳細解釋和這個答案涵蓋了。
對於奇怪的命名檔案(名稱中不包含 NUL)、帶有空格或換行符沒有問題。結果被設定在一個陣列中,這對於進一步處理很有用。
答案3
readarray
也可以從標準輸入讀取
readarray FILES <<< "$(find . -name "file*")"; echo "${#FILES[@]}"
答案4
使用shopt -s lastpipe
原始問題中顯示的解決方案有效,因為最後一個之後的部分|
與大多數腳本在同一進程中運行:
shopt -s lastpipe
# This is just the code form the original question.
# Of course it could be done with -print0, etc.
declare -a FILES
find . -name "name*" | readarray -t FILES
echo find and readarray exit status: "${PIPESTATUS[@]}"
echo "${FILES[@]}"
echo "${#FILES[@]}"
然而,只有當lastpipe
可以成為專案範圍的標準時,這才是實用的(也許您已經有了這樣的通用設置set -u
,等等)。也要知道它只lastpipe
適用於非互動式 shell。
另一種典型的管道替換< <(...)
(進程替換)存在的問題是很難檢查子進程是否成功。