パイプされたシェルスクリプトのStderrは常に表示されません。

パイプされたシェルスクリプトのStderrは常に表示されません。

私は自分のシェルスクリプトをパイピングしていくつかのテストをしていましたが、奇妙なことがわかりました。つまり、これらのパイププロセスの標準エラーが常に画面に表示されるわけではありません。

スクリプトを簡素化し、これはbashを使用したセッションです。

$ cat file1
echo stdout
echo stderr 1>&2
$ cat file2
echo stdout2
echo stderr2 1>&2
$ cat file3
echo stdout3
echo stderr3 1>&2

$ ./file1 | ./file2 | ./file3
stderr2
stderr
stdout3
stderr3

$ ./file1 | ./file2 | ./file3
stderr
stdout3
stderr3

$ ./file1 | ./file2 | ./file3
stderr2
stdout3
stderr3

file3が何も読んでいないので、最初の2つのスクリプトのstdoutが失われることを知っていますが、それは問題ではありません。

2番目と3番目のケースでは、stderr2出力はそれぞれどうなりますか?stderr

答え1

ああ、競争条件ですね!この効果を生み出すために、バックグラウンドでいくつかのタスクが実行される。

まず、プロセスがもう一方の端で閉じたパイプに書き込もうとすると、プロセスはシグナルを受け取りますSIGPIPE。デフォルトでは、プロセスは終了します。なぜこれをしたいですか?を実行すると、cat my_huge_file | head -3ファイルの最初の3行だけが表示され、シグナルメカニズムのおかげで、catこの3行以降に停止することがわかります。それ以外の場合は、ファイル全体を読み込みます。

第二に、パイプラインのすべてのプロセスは並列に実行され、どのプロセスが最初に完了するかを予測することはできません。プロセスが終了すると、パイプの読み取り終了を含むすべてのファイル記述子が閉じます。

file3たとえば、標準出力に書き込む前に完全に完了することがあります。file2その後、書き込もうとfile2すると、そのSIGPIPE行に到達せずに終了しますecho stderr2。ただし、他の場合は、完了するfile2前にstdoutへの書き込みを管理しfile3、この場合は続行できます。

関連情報