Bash:將「find」輸出透過管道傳輸到「readarray」中

Bash:將「find」輸出透過管道傳輸到「readarray」中

我正在嘗試使用 搜尋文件find,並將這些文件放入 Bash 數組中,以便我可以對它們(例如lsgrep它們)執行其他操作。但我不明白為什麼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。

另一種典型的管道替換< <(...)(進程替換)存在的問題是很難檢查子進程是否成功。

相關內容