出力をティーに配管するときのパイプ障害(141) - なぜですか?

出力をティーに配管するときのパイプ障害(141) - なぜですか?

たとえば、私の質問を明確にする必要があります。この動作は私にとって意味があります。

$ echo hi | cat
hi
$ echo hi | tee >(cat)
hi
hi

最初のケースは明らかです。 2番目のケースでは、コマンド置換を使用して「hi」をteeにパイプします。一方の「hi」はtee「d」で印刷され、もう一方の「hi」は「cat配送パイプ」で印刷されます。teeこれまではそんなに良くなった…

しかし、この場合、最初の「hi」はどうなりますか?

$ echo hi | tee >(echo yo)
yo

戻りコードは141で、パイプラインが失敗しました。原因は何ですか?

私は基本的な端末アプリケーションでMac OSX El Capitain、bashを実行しています。

答え1

私はあなたの経験を他の人が再現できるものに調整する方法を見つけたと思います。

$(エコハロー;スリップ1;エコワールド)Tシャツ>(猫)
こんにちは
こんにちは                                                       ...そしてしばらくして、
世界
世界

$エコ「$?」
0

$(エコハロー;スリップ1;エコワールド)Tシャツ>(エコヨ)|
エヤディヤ
こんにちは

$エコ「$?」
141

理解できるように、実行中のプロセスのパイプを作成します。>(command)command。標準入力はcommandコマンドライン(この場合)の他のコマンドは、開いて書き込むことができるteeパス名に接続します。いつcommandはいcat、プロセスはそこに座ってEOFを取得するまでstdinから読み込みます。この場合、tee標準入力から読み取られたすべてのデータは問題なくパイプに書き込まれます。

しかし、時commandはいecho yo、プロセスはyo標準出力に書き込まれ、すぐに終了します。これにより問題が発生しますtee。もう一方の端にプロセスがないパイプに書き込むと、SIGPIPE シグナルを受け取ります。

明らかに、OS Xバージョンはtee 最初にコマンドラインにファイルを書き込み、次に標準出力にファイルを書き込みます。したがって、あなたの例(echo hi | tee >(echo yo))では、teeパイプは最初の書き込みで失敗します。ただし、Linux および Cygwin のバージョンはtee標準出力に書き込みます。最初hiだから、死ぬ前に画面に書き込むことができます。私の拡張例では、パイプteeに書き込むときにhello死んで読んで書く機会はありませんworld

答え2

何が起こっているかを可視化するには、次の2つのバリエーションを比較します。

bash -c 'echo hi | tee >(sleep 1; echo yo); echo $?'

bash -c 'wait_and_tee () { sleep 1; tee "$@"; };
         echo hi | wait_and_tee >(echo yo); echo $?'

最初の亜種では何が起こったのか注意していますか?

$ bash -c 'echo hi | tee >(sleep 1; echo yo); echo $?'     
hi
0
$ yo

プロセス置換コマンドはsleep 1; echo yo外部コマンドと並列に実行され、bashは完了するまで待機しません。したがって、イベントの順序は次のようになります。

  • echo hi, sleep 1; echo yo3 つのコマンドがtee並列に開始されます。
  • echo hihi出力を作成します(|パイプライン)。
  • tee>(…)で作成された他のパイプから標準出力とコマンドライン引数を読み書きします。これにより、1 つのコピーがhi端末に印刷され、1 つのコピーが>(…)パイプのバッファに保存されます。
  • 前の点と平行にecho hi終了してパイプの書き込み端を閉じます|
  • tee入力ファイルの終わりに達したことがわかります。すべてのデータを記録したので終了します。
  • Bashの観点からは、パイプの両方が終了したので、コマンドは終了しました。 0が返されるので、teeパイプの状態は0です。
  • 1秒後にsleep 1終了してecho yo実行されます。

2番目のバリエーションでは、始める前に強制終了し、前を強制終了しますecho yo。今回はイベントの順番は次のとおりです。teetee

  • echo hi, echo yo3 つのコマンドがsleep 1並列に開始されます。
  • echo hihi出力を作成します(|パイプライン)。
  • 前の箇条書きと平行にecho yo印刷yoして終了します。
  • しばらくしてsleep 1終了してtee起動します。
  • tee入力から読み込み、hiそれを端末(標準出力)と引数として渡され>(…)たパイプに書き込もうとします。読み取りのためにこのパイプを開いたプロセス(echo yo)が1秒前に終了したため、パイプへの書き込み試行は失敗します。信号パイプライン(信号13、ここでシェルは 128+signal_number を報告します。)。

G-Manが説明するようにhi2番目の場合に表示されるかどうかは、tee標準出力またはファイル引数に最初に書き込もうとするかどうかによって異なります。

呼び出しがなければ、sleepタイミングはどちらの方向にも進むことができます(Linuxでは約半分/半分を取得します。他のカーネルはあるタイミングを他のものよりも可能にすることができます)。

関連情報