Bash
如果命令失敗,終止管道其餘部分的最簡潔方法是什麼?
考慮以下:
command1 | command2 | command3
據我了解管道每個命令都會執行,無論可能發生任何錯誤。當命令返回 stderr 時,它不會通過管道傳送到下一個命令,但下一個命令仍在執行(除非您使用
|&
)。我想要任何可能發生的錯誤來終止管道的其餘部分。我認為set -o pipefail
會做到這一點,但如果管道中的任何內容失敗,它只會終止管道之後可能出現的任何內容,即:(set -o pipefail; cmd1 | cmd2 && echo "I won't run if any of the previous commands fail")
那麼,如果它的任何命令失敗,最簡潔的方法是終止管道的**其餘部分是什麼?**我還需要它以失敗的命令的正確標準錯誤退出。我是從命令行上下文而不是 shell 腳本執行此操作的,因此我正在尋找簡潔的原因。想法?
我相信這是不可能的
我相信由於管道的執行方式,您所要求的並不是直接可能的。當 shell 在管道中執行“稍後”命令時,它不知道命令的成功或失敗(返回值)。它實際上同時執行所有這些,然後收集結果。
有幾個解決方法可能會有所幫助。
解決方法 1
一次執行一個命令並記憶體結果 這更好,因為如果較早的命令失敗,以後的命令絕對不會執行。
一個非常簡短的腳本範例:
cache_file=`tempfile` if command1 > $cache_file ; then command2 < $cache_file fi rm $cache_file
解決方法 2
執行所有操作,但檢查返回結果 無論如何這仍然會執行所有命令,但它確實可以讓您返回查找原因。
在這裡,每個命令的 STDERR 都被重定向到不同的文件
2>
。然後PIPESTATUS
檢查以找到每個命令的返回碼。command1 2> command1_err.log | command2 2> command2_err.log for result in ${PIPESTATUS[@]} ; do if [ $result -ne 0 ] ; then echo command failed fi done
在 shell 中執行管道的簡要概述
要創建管道,shell 遵循與以下類似的步驟:
|
使用pipe()創建每個管道() 。每個管道都帶有一個讀句柄和一個寫句柄。對於每個重新定向 (<
, ),它會打開相應的文件,使用open()>
獲取該文件的句柄。- shell為管道中的每個命令呼叫一次fork()以啟動一個新程序。
- 每個子程序將其 STDIN、STDOUT 和 STDERR 句柄交換為在 (1.) 中創建的句柄。
- 假設命令是一個外部二進製文件,每個子程序然後呼叫exec()來載入和執行二進製文件。
- 然後父程序使用wait()等待子程序完成,它還提供命令的返回值(成功或失敗)。