使 tail -f 在损坏的管道上退出

Ale*_*502 5 pipe sed tail

我想观看一个文件,直到出现一些文字

我找到了这个答案:`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
$
Run Code Online (Sandbox Code Playgroud)

按预期工作。但是当我尝试拖尾文件时

$ tail -f test.txt | sed '/w/ q'
one
two
Run Code Online (Sandbox Code Playgroud)

它永远不会退出。即使管道破裂,尾巴也不会停止。

有人知道tail退出时如何sed退出吗?

Sté*_*las 8

这与以下内容相同:

在:

cmd1 | cmd2
Run Code Online (Sandbox Code Playgroud)

cmd1即使在cmd2已经终止之后,您的外壳也恰好等到终止。并非所有贝壳都是如此。例如,有些像 Bourne 或 Korn shell 则没有。

cmd2死掉时,cmd1标准输出上的管道会被破坏,但这不会cmd1立即终止。

cmd1将在下次尝试写入该管道时终止。然后它将收到一个 SIGPIPE,其默认操作是终止进程。

使用cmd1==tail -f filecmd2== sed /w/qtail -f将读取文件的最后 10 行并将它们写入标准输出(管道),通常在一个块中,除非行非常大并且坐在那里等待更多文本被附加到file.

sed,它同时运行,将等待其标准输入上的输入,读取它,逐行处理它,如果有一行包含w.

一旦sed找到该行(或者可能有一些实现的单行延迟),它就会退出,但在那个时候,tail它已经将它必须写入的所有内容写入管道,因此它不会收到 SIGPIPE,除非稍后将一些附加文本添加到文件中(此时它将执行致命的操作write())。

如果您想cmd1在终止后立即cmd2终止,则一旦cmd2终止,您就需要一些东西来杀死它 。喜欢:

sh -c 'echo "$$"; exec tail -f test.txt' | {
  IFS= read pid
  sed /w/q
  kill -s PIPE "$pid"
}
Run Code Online (Sandbox Code Playgroud)

或与bash

{ sed /w/q; kill -s PIPE "$!"; } < <(exec tail -f text.txt)
  
Run Code Online (Sandbox Code Playgroud)

2020 编辑

正如@user414777 所指出的那样,从 8.28 版开始,GNU 实现tail在跟随模式下,现在不仅轮询它监视的文件中的新数据,而且还检查其标准输出是否成为损坏的管道并立即退出(或者在不使用inotify 的情况下在一秒钟内),使上面的那些变通方法变得不必要。

但是请注意,只有 GNU 进行tail这种处理,而不是tail(AFAIK) 的任何其他实现,也不是任何其他实用程序(甚至 GNU 实现)。

所以,虽然:

tail -f file | head -n1
Run Code Online (Sandbox Code Playgroud)

一行后退出,

tail -f file | tr '[:lower:]' '[:upper:]' | head -n1
Run Code Online (Sandbox Code Playgroud)

例如,它不一定tr会监视它的标准输出是否成为损坏的管道,因此只有在管道像往常一样损坏后在那里一些东西时才会死(并且只有在那时GNUtail -f才会退出)。

  • 请参阅[此处](https://unix.stackexchange.com/a/616346/414777)关于 GNU tail 所做的特殊事情的答案,以便 `tail -f | sed /pat/q` (有时)有效。 (2认同)