なぜ `while true; do echo y; done | true` は自動的に終了するのに、 `while true; do echo y | cat; done | true` は終了しないのでしょうか?

なぜ `while true; do echo y; done | true` は自動的に終了するのに、 `while true; do echo y | cat; done | true` は終了しないのでしょうか?

タイトルの質問を明確にすると、前者がなぜ終了するのかは理解できます。後者が| catループ本体に を追加しただけで終了しない理由は理解できません。

また、おそらく関連していると思われるが、

while true; do echo y; done

私がそうするとすぐに死ぬ^Cが、殺す

while true; do echo y | cat; done

多くの場合、複数回攻撃する必要があります^C。 1 回で効果が出る場合もあれば、2 回または 3 回で効果が出る場合もありますが、^Cしばらく押し続けて効果がなくなる場合もあります。

両方の動作は bash と zsh の両方で発生しますが、^Cbash ではどちらか一方がよりまれなようです。

どちらの動作でも、これはパイプの追加に限定されませんcat| dd| teeなどもそれらを引き起こします。echo y | trueループ本体にパイプが存在すると、それが発生します。

ループ本体にパイプが存在すると、ループの信号に対する応答が変わるのはなぜですか?

答え1

ではwhile true; do echo y; done | true、が組み込まれているため、ループ内でパイプにecho書き込むサブシェル プロセスを実行します。y\n

が戻るとtrue、そのパイプの読み取り側は閉じられるので、書き込み側に書き込むと、SIGPIPE書き込みプロセスに が配信されます。ここでは、ループを実行しているサブシェル プロセスです。

ではwhile true; do echo y | cat; done | truecatパイプに書き込むのは です。catは一般には組み込まれておらず、組み込まれていたとしても、zsh と ksh 以外のシェルでは、すべてのパイプ コンポーネントは常に子プロセスで実行されます。

したがって、ここでは実行中のプロセスのみcatが終了し、ループを実行するサブシェル プロセスは、stdout にcat書き込むとすぐに終了する他のプロセスの実行を続行します。y\n

ksh93/ksh2020 では、次のようにします。

$ builtin cat
$ type cat
cat is a shell builtin
$ set -o pipefail
$ while true; do echo y | cat; done | true; kill -l "$?"
PIPE

今回は、catが組み込まれており、ループと同じプロセスで実行されます (cat最初のパイプラインの右端のコマンドも同様で、ksh はサブシェルでそのコマンドを実行しません)。そのため、サブシェルはtrueexit にパイプし、kill -lSIGPIPE によって強制終了されたことを確認します。

関連情報