與 PIPE 一起使用的 Bash 退出狀態

與 PIPE 一起使用的 Bash 退出狀態

我試圖了解使用管道時如何傳達退出狀態。假設我用來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 pipefailsetopt 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

如您所見,管道的退出狀態是其最後一個命令的退出狀態。

相關內容