PIPEで使用するためのBash終了ステータス

PIPEで使用するためのBash終了ステータス

パイプを使用するときに終了状態を渡す方法を理解しようとしています。which存在しないプログラムを見つけるために使用しているとします。

which lss
echo $?
1

私のシャットダウンステータスは、検索にwhich失敗したため1です。lssこれはいいですね。しかし、次のようにしようとすると:

which lss | echo $?
0

これは、最後に実行されたコマンドが正常に終了したことを示します。私が理解できる唯一の方法は、PIPEが終了ステータスを生成できることです。これは正しい理解方法ですか?

答え1

パイプの終了状態は右コマンドの終了状態です。左コマンドの終了状態は無視されます。

which lss | echo $?これは表示されません。which lss | true; echo $?これを表示するには実行する必要があります。which lss | echo $?このecho $?パイプラインより前の最後のコマンドステータスが報告されます。)

シェルがこれを行う理由は、左側のエラーを無視する必要がある非常に一般的な状況があるためです。左がまだ書いている間に右が出ると(またはより一般的には標準入力を閉じる)、左はSIGPIPE信号を受け取ります。この場合、一般的に問題はありません。右はデータに興味がなく、右はデータに興味がありません。左側の操作がこのデータを生成するだけであれば、停止できます。

ただし、SIGPIPE以外の理由で左側が死んだ場合、または標準出力からデータを生成する以上の操作を実行している場合は、左側のエラーは実際のエラーであるため報告する必要があります。

通常のshで唯一の解決策は、名前付きパイプを使用することです。

set -e
mkfifo p
command1 >p & pid1=$!
command2 <p
wait $pid1

ksh、bash、およびzshでは、パイプのコンポーネントがゼロ以外の状態で終了した場合は、ゼロ以外の状態でパイプを終了するようにシェルに指示できます。オプションを設定する必要がありますpipefail

  • クッシュ:set -o pipefail
  • 大きな打撃:shopt -s pipefail
  • zsh:(setopt pipefailまたはsetopt pipe_fail

PIPESTATUSmksh、bash、およびzshでは、最後のパイプライン(一般化)pipestatusのすべてのコマンド状態を含む配列である変数(bash、mksh)または(zsh)を使用して、パイプラインの各コンポーネントの状態を取得できます。$?

答え2

パイプの終了ステータスはパイプラインの最後のコマンドの終了ステータスです(シェルオプションがそれをサポートするシェルに設定されていないpipefail場合、終了ステータスはパイプラインの最後のコマンドの終了ステータスになります)。 。

パイプの終了状態を印刷できません。内からパイプは、パイプが実際に実行を完了するまで値が何であるかを知る方法がないためです。

管路

which lss | echo $?

標準入力を読み取ることができないので意味がありませんecho(パイプは1つのコマンドの出力と次のコマンドの入力の間でデータを渡すために使用されます)。また、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

~によるとこのスタックオーバーフローのQ&A、パイプを使用するときにコマンドの終了ステータスを印刷するには、次の操作を行います。

your_command | echo $PIPESTATUS

または、コマンドを使用してパイプライン障害を設定set -o pipefail(設定解除、実行set +o pipefail)し、代わりに$?を使用します$PIPESTATUS

your_command | echo $?

このコマンドは、少なくとも一度実行した場合にのみ機能します。つまり、PIPESTATUS次のようにパイプを使用してコマンドの後に実行する場合にのみ機能します。

command_which_exit_10 | command_which_exit_1
echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"

${PIPESTATUS[0]}10ポイントと1ポイントを受け取ります${PIPESTATUS[1]}。バラより今回のU&L Q&Aより多くの情報を知りたいです。

答え4

シェルはパイプラインを実行する前にコマンドラインを評価します。$?パイプラインの前に実行された最後のコマンドの値も同じです。echo以前に開始されてから開始されたことは重要ではありwhichません。

これで、質問が終了ステータスの伝播に関するものである場合は、次の基本的な動作を確認できます。

% false | true
% echo $?
0

% true | false
% echo $?
1

ご覧のとおり、パイプラインの終了状態は最後のコマンドの終了状態です。

関連情報