web-dev-qa-db-ja.com

bashパイプラインで「yes」を使用すると、なぜ無限ループが発生しないのですか?

ドキュメントによると、bashはパイプライン内のすべてのコマンドの実行が完了するまで待機してから続行します

シェルは、パイプライン内のすべてのコマンドが終了するのを待ってから、値を返します。

では、なぜコマンドyes | trueすぐに終了しますか?yesが永久にループし、パイプラインが戻らないようにする必要はありませんか?


そして、サブ質問: POSIX仕様 によると、シェルパイプラインは、最後のコマンドが完了した後に戻るか、すべてのコマンドが完了するまで待機するかを選択できます。一般的なシェルはこの意味で異なる動作をしますか? yes | trueは永久にループしますか?

16
hugomg

trueが終了すると、パイプの読み取り側は閉じられますが、yesは書き込み側への書き込みを続行します。この状態は「壊れたパイプ」と呼ばれ、カーネルがSIGPIPE信号をyesに送信します。 yesはこのシグナルについて特別なことは何もしないため、強制終了されます。シグナルを無視した場合、そのwrite呼び出しはエラーコードEPIPEで失敗します。これを行うプログラムは、EPIPEに気づいて書き込みを停止する準備をする必要があります。そうしないと、無限ループに入ります。

もし、するなら strace yes | true1 両方の可能性に備えてカーネルが準備していることがわかります。

write(1, "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 4096) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=17556, si_uid=1000} ---
+++ killed by SIGPIPE +++

straceは、デバッガーAPIを介してイベントを監視しています。デバッガーAPIは、最初にエラーを返すシステムコールについて通知し、次にシグナルについて通知します。ただし、yesの観点から見ると、シグナルが最初に発生します。 (技術的には、シグナルはカーネルがユーザースペースに制御を返した後、マシン命令が実行される前に配信されるため、Cライブラリのwrite "ラッパー"関数はerrnoを設定してアプリケーションに戻る機会がありません。 )


1 悲しいことに、straceはLinux固有です。最近のほとんどのUnixには、似たようなsomeコマンドがありますが、名前が異なる場合が多く、syscall引数が完全にデコードされず、ルートでのみ機能する場合があります。

33
casey

あるシェルはありますか? trueは永久にループしますか?

おそらく、yesコマンドはパイプを使用しているため、パイプが壊れると失敗します。一方、sleepはパイプを使用しないため、次のようになります。

sleep 100000000 | true

少なくとも100000000秒間実行されます。

5
muru