パイプライン内の非最終コマンドの終了ステータスをキャプチャするにはどうすればよいですか?

パイプライン内の非最終コマンドの終了ステータスをキャプチャするにはどうすればよいですか?

パイプラインのどこかで実行されるコマンドの終了ステータスをキャプチャしたい前に最後の位置。例えば、パイプラインが次のようなものである場合

command_1 ... | command_2 ... | command_3 ... | ... | command_n

... 、 、などcommand_1の終了ステータスをキャプチャする方法を知りたいです。 (もちろん、の終了ステータスをキャプチャするのは簡単です。)command_2command_3command_n

また、重要であれば、このパイプラインは zsh シェル関数内で実行されます。


の終了ステータスをcommand_1次のようにキャプチャしようとしました

function_with_pipeline () {

    local command_1_status=-999999  # sentinel value

    { command_1 ...; command_1_status=$? } | command_2 ... | ... | command_n
    ...

}

...しかし、パイプラインを実行した後も、変数の値はcommand_1_statusセンチネル値のままでした。


ちなみに、パイプラインにコマンドが 2 つしかない場合の実際の例を次に示します。

foo ... | grep ...

fooは、この例のために定義された関数です。

foo () {

    (( $1 & 1 )) && echo "a non-neglible message"
    (( $1 & 2 )) && echo "a negligible message"
    (( $1 & 4 )) && echo "error message" >&2

    return $(( ( $1 & 4 ) >> 2 ))
}

foo目標は、パイプライン内の呼び出しの終了ステータスをキャプチャすることです。

この関数は、function_with_pipelineこれを実現するために、上で説明した (最終的には効果のない) 戦略を実装します。

function_with_pipeline () {

    local foo_status=-999999  # sentinel value

    { foo $1; foo_status=$? } | grep -v "a negligible message"

    printf '%d\ndesired: %d; actual: %d\n\n' $1 $(( ( $1 & 4 ) >> 2 )) $foo_status

}

以下のループは関数を実行しますfunction_with_pipeline。出力は、ローカル変数の値がfoo_status開始時と変わらないことを示しています。

for i in $(seq 0 7)
do
    function_with_pipeline $i
done
# 0
# desired: 0; actual: -999999
# 
# a non-neglible message
# 1
# desired: 0; actual: -999999
# 
# 2
# desired: 0; actual: -999999
# 
# a non-neglible message
# 3
# desired: 0; actual: -999999
# 
# error message
# 4
# desired: 1; actual: -999999
# 
# error message
# a non-neglible message
# 5
# desired: 1; actual: -999999
# 
# error message
# 6
# desired: 1; actual: -999999
# 
# error message
# a non-neglible message
# 7
# desired: 1; actual: -999999
#

localの定義で宣言を省略した場合も同じ結果が得られますfoo_status

答え1

pipestatusにはそのための特別な配列があるのでzsh、試してみてください。

command_1 ... | command_2 ... | command_3

そして

echo $pipestatus[1] $pipestatus[2] $pipestatus[3]

あなたのアプローチが機能しない理由は、各パイプが個別のサブシェルで実行され、サブシェルを終了すると破棄される独自の変数を持つためです。


参考までに、PIPESTATUS(大文字で)ですbash

答え2

mispipeどのシェルでも動作します。構文は、(通常のパイプと比較して) 次のように動作します。

mispipe true false ; echo $?  # returns exit code of 1st command `true`
true | false ; echo $?  # returns exit code of 2nd command `false`

出力:

0
1

プログラムが 2 つ以上ある場合の対処方法:

# this still returns exit code of 1st command `true`
mispipe true 'false | false | false' ; echo $?

出力:

0

目に見える がないにもかかわらず|、パイプとしての動作は正常です。

yes | mispipe head 'wc -c'

出力:

     20

関連情報