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


16

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

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

では、なぜコマンドはyes | trueすぐに終了するのですか?すべきではないyesループが永遠に続き、パイプラインが戻らか?


サブ質問:POSIX仕様によれば、シェルパイプラインは、最後のコマンドが終了した後に戻るか、すべてのコマンドが終了するまで待機するかを選択できます。この意味で、共通のシェルは異なる動作をしますか?yes | true永遠にループするシェルはありますか?


yes | tee >(true) >/dev/nullteeすべてのライターが死ぬまで継続するので、期待どおりに動作しますtrue。そのため、終了しても完全に混乱することはありません。
チャールズダフィー

1
true基本的に{return 0;}プログラムなので、永遠に言うまでもなく、長い間実行されるとは思いません。
ドミトリーグリゴリエフ

回答:


33

ときに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ただし、の観点からは、信号が最初に発生します。(技術的には、カーネルが制御をユーザー空間に戻した後、それ以上のマシン命令が実行される前にシグナルが配信されるためwrite、Cライブラリの「ラッパー」関数errnoはアプリケーションに設定して戻る機会を得ません。)


1悲しいことに、straceLinux固有です。ほとんどの最近のUnixには、似たようなことをするコマンドがありますが、多くの場合、名前が異なります。おそらく、syscall引数を完全にデコードしません。


3
@hugomgの場合、パイプはまったく無関係です。
ムル

3
@hugomg yesは、パイプに接続されているものがないためです。
ムル

4
確かに、文書化された動作のデモです。「パイプラインを終了する前にすべてのコマンドが終了するまで待機します」。yes書き込み先のFDはパイプに接続されていないため、SIGPIPEを取得できなくなります。
トム・ハント

2
@hugomg、永遠にループしているのと同じ方法でyes >/dev/null永遠にループしています。単純なコマンドにも当てはまらないパイプラインについては何も示しません(終了を待つ動作が単純なコマンドにも当てはまるため)。
チャールズダフィー

2
@zwol:ここで少し異なる意味を持つ用語を使用している、または少し異なる視点から物事を考えていると思いますが、どちらの場合でもwrite()(libcの関数)は戻りません(それに続くPCに制御を転送します)シグナルハンドラーは実行されましたが、シグナルハンドラーはプログラムを終了するため、制御は転送されず、したがってwrite()戻りません。はい、それはいくつかのxxx_write()関数return を持つことでカーネルに実装されています-EPIPEが、私たちはユーザースペースプログラムをデバッグしていて、それに興味はありません。
ディートリッヒエップ

5

はいのシェルはありますか?trueは永遠にループしますか?

まれに、yesコマンドがパイプを使用しているため、パイプが破損すると失敗します。sleep一方、パイプを使用しないため、次のようにします。

sleep 100000000 | true

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


2
パイプ内の最後の(右端の)組み込みコマンドを分岐しないすべての最新のシェルtrueと、組み込みの場所に注意してください。これは最近のバージョンに適用されますBourne Shellksh93zsh。この^Zようなコマンドの実行中にヒットすると、スリープが一時停止し、シェルは外部の助けなしでは回復できなくなります。
気味悪い

3
zsh 4.3.4(i386-pc-solaris2.11)はここにあるため、これは最近変更されたようです。興味深いアイデアは、Bourne Shellに対して同様の修正を実装できるかどうかを確認する必要があります。Bourne Shellのように、どのttyプロセスグループがどのように使用されるのかという疑問が残っています。最も近いコマンドが組み込みコマンドであるという事実は、スリープ用のプロセスグループが既に設定された後に発見されます。
気味悪い

2
@CharlesDuffyは私が理解していることから、schilyのshのバージョンを保守しており、これに最新のシェルからの改良をバックポートしています。彼はどこかでそれについてここに投稿しています。
ムル

3
家宝アーカイブのボーンシェルは〜2007年まで維持されましたが、まだへの呼び出しを含んでいるので完全に移植可能にされませんでしたsbrk()。ポータブルで保守されたバージョンは、schilyツールバンドルに含まれており、@ Charles Duffyはすでに情報の場所を発見しました;-)
schily

2
Bourne Shellにバックポートした機能の多くは、@ muru bsh(VBERTOSのBerthold Shell、UNOSの仮想メモリ拡張バージョン-最初のUNIXクローン)のものです。Bshは1984年と1985年に多くのcsh機能を取得しましたが、UNOSのエイリアスメカニズムは1980年のcshのエイリアスメカニズムよりも優れていました。その他の新しいBourne Shell機能はPOSIXからのもので、POSIX準拠に近づきます。
schily
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.