我有一個腳本,它使用以下方法將文件名解析為數組:關於SO的問答:
unset ARGS
ARGID="1"
while IFS= read -r -d $'\0' FILE; do
ARGS[ARGID++]="$FILE"
done < <(find "$@" -type f -name '*.txt' -print0)
這非常有效,可以完美處理所有類型的檔案名稱變體。然而,有時我會將一個不存在的檔案傳遞給腳本,例如:
$ findscript.sh existingfolder nonexistingfolder
find: `nonexistingfile': No such file or directory
...
在正常情況下,我會讓腳本捕獲類似的退出代碼RET=$?
,並用它來決定如何繼續。這似乎不適用於上面的過程替換。
在這種情況下,正確的程序是什麼?如何捕捉返回碼?是否有其他更合適的方法來確定替換過程中是否出現問題?
答案1
您可以透過在其標準輸出上回顯任何子 shell 進程的傳回值來輕鬆取得其傳回值。進程替換也是如此:
while IFS= read -r -d $'\0' FILE ||
! return=$FILE
do ARGS[ARGID++]="$FILE"
done < <(find . -type f -print0; printf "$?")
如果我運行它,那麼最後一行 -(或者\0
定界部分,視情況而定)將是find
的返回狀態。read
當它收到 EOF 時將返回 1 - 因此唯一$return
設定為 的時間$FILE
是讀入訊息的最後一位。
我過去printf
常常避免添加額外的\n
ewline - 這很重要,因為即使是read
定期執行的 - 不以\0
NUL 分隔的 - 在剛剛讀入的數據不以 結尾的情況下,也會返回 0 以外的值一條\n
線。因此,如果您的最後一行不以\n
ewline 結尾,則讀入變數中的最後一個值將是您的回傳值。
運行上面的命令,然後:
echo "$return"
輸出
0
如果我改變進程替換部分......
...
done < <(! find . -type f -print0; printf "$?")
echo "$return"
輸出
1
更簡單的示範:
printf \\n%s list of lines printed to pipe |
while read v || ! echo "$v"
do :; done
輸出
pipe
事實上,只要您想要的返回是您從進程替換中寫入 stdout 的最後一個內容(或者您以這種方式從中讀取的任何子外殼進程),那麼就$FILE
始終是您想要的返回狀態是通過了。因此該|| ! return=...
部分並不是絕對必要的 - 它僅用於演示概念。
答案2
用一個協程。使用coproc
內建函數,您可以啟動子進程,讀取其輸出並檢查其退出狀態:
coproc LS { ls existingdir; }
LS_PID_=$LS_PID
while IFS= read i; do echo "$i"; done <&"$LS"
wait "$LS_PID_"; echo $?
如果該目錄不存在,wait
將以非零狀態代碼退出。
目前需要將 PID 複製到另一個變量,因為$LS_PID
之前會被取消設置wait
在呼叫。看在我可以等待 coproc 之前,Bash 取消設定 *_PID 變量了解詳情。
答案3
進程替換中的進程是異步的:shell 啟動它們,然後不提供任何方式來偵測它們何時終止。因此您將無法獲得退出狀態。
您可以將退出狀態寫入文件,但這通常很笨拙,因為您無法知道文件何時寫入。在這裡,文件是在循環結束後不久寫入的,因此等待它是合理的。
… < <(find …; echo $? >find.status.tmp; mv find.status.tmp find.status)
while ! [ -e find.status ]; do sleep 1; done
find_status=$(cat find.status; rm find.status)
另一種方法是使用命名管道和後台進程(您可以wait
使用)。
mkfifo find_pipe
find … >find_pipe &
find_pid=$!
… <find_pipe
wait $find_pid
find_status=$?
如果兩種方法都不合適,我認為您需要使用一種功能更強大的語言,例如 Perl、Python 或 Ruby。
答案4
一種方法是:
status=0
token="WzNZY3CjqF3qkasn" # some random string
while read line; do
if [[ "$line" =~ $token:([[:digit:]]+) ]]; then
status="${BASH_REMATCH[1]}"
else
echo "$line"
fi
done < <(command; echo "$token:$?")
echo "Return code: $status"
這個想法是在命令完成後回顯退出狀態以及隨機令牌,然後使用 bash 正規表示式查找並提取退出狀態。此標記用於建立要在輸出中尋找的唯一字串。
從一般程式設計意義上來說,這可能不是最好的方法,但它可能是在 bash 中處理它的最不痛苦的方法。