
我試圖了解使用管道時如何傳達退出狀態。假設我用來which
定位一個不存在的程式:
which lss
echo $?
1
由於which
未能找到,lss
我的退出狀態為 1。但是,當我嘗試以下操作時:
which lss | echo $?
0
這表示最後執行的命令已經正常退出。我能理解這一點的唯一方法是,也許 PIPE 也會產生退出狀態。這是正確的理解方式嗎?
答案1
管道的退出狀態是右側指令的退出狀態。左側指令的退出狀態將被忽略。
(請注意,which lss | echo $?
不會顯示此內容。您將運行which lss | true; echo $?
以顯示此內容。在 中which lss | echo $?
,echo $?
報告此管道之前的最後一個命令的狀態。)
shell 這樣做的原因是,有一個相當常見的情況,即應忽略左側的錯誤。如果右側退出(或更一般地關閉其標準輸入)而左側仍在寫入,則左側會收到 SIGPIPE 訊號。在這種情況下,通常沒有什麼問題:右側不關心資料;右側不關心資料;右側不關心資料。如果左側的作用只是產生這些數據,那麼它可以停止。
但是,如果左側由於 SIGPIPE 之外的某種原因而死亡,或者如果左側的工作不僅僅是在標準輸出上產生數據,那麼左側的錯誤就是真正的錯誤,應該報告。
在普通 sh 中,唯一的解決方案是使用命名管道。
set -e
mkfifo p
command1 >p & pid1=$!
command2 <p
wait $pid1
在 ksh、bash 和 zsh 中,如果管道的任何元件以非零狀態退出,您可以指示 shell 以非零狀態退出管道。您需要設定pipefail
選項:
- 克什:
set -o pipefail
- 重擊:
shopt -s pipefail
- zsh:(
setopt pipefail
或setopt pipe_fail
)
PIPESTATUS
在 mksh、bash 和 zsh 中,您可以使用變數(bash、mksh) 或(zsh)來取得管道中每個元件的狀態pipestatus
,該變數是一個數組,其中包含最後一個管道中所有命令的狀態(概括為$?
) 。
答案2
管道的退出狀態是管道中最後一個命令的退出狀態(除非pipefail
在支援它的 shell 中設定了 shell 選項,在這種情況下,退出狀態將是管道中最後一個命令退出的狀態)非零狀態)。
無法輸出管道的退出狀態從內部管道,因為在管道實際完成執行之前無法知道該值是什麼。
管道
which lss | echo $?
是無意義的,因為echo
不讀取其標準輸入(管道用於在一個命令的輸出到下一個命令的輸入之間傳遞資料)。也不會echo
列印管道的退出狀態,而是立即執行指令的退出狀態前管道。
$ false
$ which hello | echo $?
1
which: hello: Command not found.
$ true
$ which hello | echo $?
0
which: hello: Command not found.
這是一個更好的例子:
$ echo hello | read a
$ echo $?
0
$ echo nonexistent | { read filename && rm $filename; }
rm: nonexistent: No such file or directory
$ echo $?
1
這意味著管道也可以與以下各項一起使用if
:
if gzip -dc file.gz | grep -q 'something'; then
echo 'something was found'
fi
答案3
是的,PIPE 產生退出狀態;如果您想要 PIPE 之前的命令退出程式碼,您可以使用$PIPESTATUS
根據這個堆疊溢位問答,要在使用管道時列印命令的退出狀態,您可以執行以下操作:
your_command | echo $PIPESTATUS
或使用指令設定 pipelinefail set -o pipefail
(要取消設置,請執行set +o pipefail
),然後使用$?
取代$PIPESTATUS
。
your_command | echo $?
這些命令只有在您至少運行一次後才有效;這意味著PIPESTATUS
僅當您在使用管道的命令之後運行它時才有效,如下所示:
command_which_exit_10 | command_which_exit_1
echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
您將獲得 10 分${PIPESTATUS[0]}
和 1 分${PIPESTATUS[1]}
。看本 U&L 問答了解更多。
答案4
shell 在執行管道之前評估命令列。$?
管道之前執行的最後一個命令的值也是如此。其echo
本身是在之前還是之後啟動是which
無關緊要的。
現在,如果您的問題具體是關於退出狀態的傳播,您可以像這樣研究預設行為:
% false | true
% echo $?
0
% true | false
% echo $?
1
如您所見,管道的退出狀態是其最後一個命令的退出狀態。