Bash

如果命令失敗,終止管道其餘部分的最簡潔方法是什麼?

  • May 9, 2019

考慮以下:

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 遵循與以下類似的步驟:

  1. |使用pipe()創建每個管道() 。每個管道都帶有一個讀句柄和一個寫句柄。對於每個重新定向 ( <, ),它會打開相應的文件,使用open()>獲取該文件的句柄。
  2. shell為管道中的每個命令呼叫一次fork()以啟動一個新程序。
  3. 每個子程序將其 STDIN、STDOUT 和 STDERR 句柄交換為在 (1.) 中創建的句柄。
  4. 假設命令是一個外部二進製文件,每個子程序然後呼叫exec()來載入和執行二進製文件。
  5. 然後父程序使用wait()等待子程序完成,它還提供命令的返回值(成功或失敗)。

引用自:https://unix.stackexchange.com/questions/517969