以下のコマンドが終了しないのはなぜですか?ループは終了するのではなく、無期限に実行されます。
より複雑なセットアップを使用してこの動作を発見しましたが、コマンドの最も単純な形式は次のようになります。
終了しません:
while /usr/bin/true ; do echo "ok" | cat ; done | exit 1
上記のタイプミスはありません。各 '|'パイプです。 「exit1」は、実行されて終了した別のプロセスを表します。
「exit1」によってwhileループ(リーダーのないパイプに書き込む)でSIGPIPEが発生し、ループが発生することを期待しています。ただし、ループは実行を継続します。
コマンドが停止しないのはなぜですか?
これは、実装の選択によるものです。
ksh93
を使用してSolarisで同じスクリプトを実行すると、異なる動作が発生します。
$ while /usr/bin/true ; do echo "ok" | cat ; done | exit 1
cat: write error [Broken pipe]
問題を引き起こすのは内部パイプラインです。それがないと、シェル/ OSが何であれループが終了します。
$ while /usr/bin/true ; do echo "ok" ; done | exit 1
$
cat
はbashでSIGPIPEシグナルを取得していますが、シェルはとにかくループを繰り返しています。
Process 5659 suspended
[pid 28801] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28801] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28801 detached
Process 28800 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28802 attached
Process 28803 attached
[pid 28803] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
Process 5659 suspended
[pid 28803] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28803 detached
Process 28802 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Process 28804 attached
Process 28805 attached (waiting for parent)
Process 28805 resumed (parent 5659 ready)
Process 5659 suspended
[pid 28805] execve("/bin/cat", ["cat"], [/* 63 vars */]) = 0
[pid 28805] --- SIGPIPE (Broken pipe) @ 0 (0) ---
Process 5659 resumed
Process 28805 detached
Process 28804 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
Bashドキュメントの状態:
シェルすべてのコマンドを待機パイプラインで終了してから値を返します。
Kshドキュメントの状態:
おそらく最後を除いて、各コマンドは個別のプロセスとして実行されます。シェル最後のコマンドを待つ終了します。
[〜#〜] posix [〜#〜]状態:
パイプラインがバックグラウンドにない場合(非同期リストを参照)、シェル最後のコマンドを待機する必要がありますパイプラインで指定された完了、およびすべてのコマンドを待機する場合もあります完了します。
この問題は何年もの間私を悩ませてきました。正しい方向にナッジしてくれたjiliagreに感謝します。
質問を少し言い換えると、私のLinuxボックスでは、これは期待どおりに終了します。
while true ; do echo "ok"; done | head
しかし、パイプを追加すると、期待どおりに終了しますnot:
while true ; do echo "ok" | cat; done | head
それは何年もの間私を苛立たせました。 jilliagreによって書かれた答えを検討することによって、私はこの素晴らしい修正を思いつきました:
while true ; do echo "ok" | cat || exit; done | head
Q.E.D. .。
まあ、完全ではありません。これはもう少し複雑なものです:
i=0
while true; do
i=`expr $i + 1`
echo "$i" | grep '0$' || exit
done | head
これは正しく機能しません。 || exit
を追加したので、早期に終了する方法がわかりますが、最初のecho
がgrep
と一致しないため、ループはすぐに終了します。この場合、grep
の終了ステータスには実際には関心がありません。私の回避策は、別のcat
を追加することです。したがって、これは「tens」と呼ばれる不自然なスクリプトです。
#!/bin/bash
i=0
while true; do
i=`expr $i + 1`
echo "$i" | grep '0$' | cat || exit
done
これは、tens | head
として実行すると適切に終了します。ああ、助かった。