私はこれが不可能だと信じています

私はこれが不可能だと信じています

以下を考慮してください。

command1 | command2 | command3

私が理解したように、パイプラインは発生する可能性のあるエラーに関係なくすべてのコマンドを実行します。コマンドがstderrを返すと、次のコマンドにパイプされませんが、次のコマンドは引き続き実行されます(|&使用しない限り)。発生する可能性のあるエラーがある場合は、残りのパイプラインを終了したいと思います。私はこれを達成することが可能だと思いますset -o pipefailが、パイプのどんなものでも失敗した場合、パイプの後ろに来ることができるすべてを終了します。つまり:

(set -o pipefail; cmd1 | cmd2 && echo "I won't run if any of the previous commands fail")

したがって、終了する最も簡単な方法は残りコマンドが失敗した場合、パイプラインはどうなりますか?また、失敗したコマンドに対して正しいstderrを使用して終了する必要があります。私はシェルスクリプトではなくコマンドラインコンテキストでこれを行うので、単純さを探しています。アイデア?

ベストアンサー1

私はこれが不可能だと信じています

私はあなたが要求することがパイプの実装方法のために直接可能ではないと思います。シェルがパイプラインで「後で」コマンドを実行すると、コマンドの成功または失敗(戻り値)がわかりません。実際にはすべて同時に実行し、結果を収集します。

役に立つかもしれないいくつかの回避策があります。

ソリューション1

一度に 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

シェルでパイプラインを実行するための簡単な概要

パイプを作成するために、シェルは次の手順に従います。〜のようにこれら:

  1. 次のコマンド(|)を使用して各パイプを作成します。管路()。各パイプには読み取りハンドルと書き込みハンドルがあります。各リダイレクト(<、、>)に対してそのファイルを開き、それを使用してファイルへのハンドルを取得します。開いている()
  2. シェル呼び出しクロス()パイプラインの各コマンドは新しいプロセスを開始します。
  3. 各サブプロセスは、(1.)で生成されたハンドルとSTDIN、STDOUT、およびSTDERRハンドルを交換します。
  4. コマンドが外部バイナリであると仮定すると、各子プロセスは以下を呼び出します。実装する()バイナリをロードして実行します。
  5. その後、親プロセスは子プロセスが使用を終了するのを待ちます。待つ()また、コマンドの戻り値(成功または失敗)も提供します。

おすすめ記事