web-dev-qa-db-ja.com

壊れたパイプでtail -fを終了する

少しテキストが表示されるまでファイルを見たい

私はこの答えを見つけました: `tail -f`テキストが表示されるまで

しかし、私がubuntuでそれを試したとき、それは終了しません:

$ echo one > test.txt
$ echo two >> test.txt
$ echo three >> test.txt
$ echo four >> test.txt
$ cat test.txt | sed '/w/ q'
one
two
$

期待どおりに動作します。ただし、ファイルをテールしようとすると

$ tail -f test.txt | sed '/w/ q'
one
two

それは決して存在しません。パイプが壊れてもテールが止まらない。

tailが終了したときにsedを終了させる方法を知っている人はいますか?

5
Alex028502

それはと同じものです:

に:

_cmd1 | cmd2
_

シェルは、_cmd1_がすでに終了した後でも、_cmd2_が終了するまで待ちます。これはすべてのシェルに当てはまるわけではありません。たとえば、ボーンやコーンシェルのようなものはそうではありません。

_cmd2_が終了すると、_cmd1_のstdoutのパイプはbrokenになりますが、これは_cmd1_を即座に終了しません。

_cmd1_は、次にそのパイプに書き込もうとしたときに終了します。次に、デフォルトのアクションがプロセスの終了であるSIGPIPEを受け取ります。

_cmd1_ == _tail -f file_および_cmd2_ == _sed /w/q_を使用すると、_tail -f_はファイルの最後の10行を読み取り、それらをstdout(パイプ)に書き込みます、通常、行が本当に大きく、fileに追加されるテキストが追加されるのを待ってそこに座っている場合を除き、1つのチャンクになります。

同時に実行されるsedは、stdinでの入力を待機し、それを読み取り、1行ずつ処理し、wを含む行がある場合は終了します。

その行が見つかるとすぐに(またはsedの実装により1行の遅延が発生する可能性があります)、その行は終了しますが、その時点でtailはすでにすべてのパイプに書き込みを行っていますWRITEに変換するため、後でファイルに追加のテキストが追加されない限り、SIGPIPEを受信しません(その時点で、致命的なwrite()が実行されます)。

_cmd1_が終了するとすぐに_cmd2_を終了させたい場合は、_cmd2_が終了したら何かを強制終了する必要があります。同様に:

_sh -c 'echo "$$"; exec tail -f test.txt' | {
  IFS= read pid
  sed /w/q
  kill -s PIPE "$pid"
}
_

またはbashを使用:

_{ sed /w/q; kill -s PIPE "$!"; } < <(exec tail -f text.txt)
_
8