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

使 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

答案1

这与以下内容相同:

在:

cmd1 | cmd2

cmd1即使您的 shellcmd2已经终止,它也会一直等到终止。并非所有贝壳都是这样。例如,有些像 Bourne 或 Korn shell 就没有。

cmd2死亡时,标准输出上的管道cmd1变成破碎的,但这不会cmd1立即终止。

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

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

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

一旦它发现该行(或者可能在某些实现中存在单行延迟sed),它就会退出,但那时,它tail已经将所有需要写入的内容写入管道,因此它不会收到 SIGPIPE,除非稍后会在文件中添加一些附加文本(此时它将执行 fatal 操作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)
  

2020 编辑

正如 @user414777 所指出的,自版本 8.28 以来,在跟随模式下的 GNU 实现tail现在不仅会轮询其监视的文件中的新数据,还会检查其 stdout 是否变成损坏的管道并立即退出(或在一秒内,如果inotify未使用),使得上述解决方法变得不必要。

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

所以,同时:

tail -f file | head -n1

一行后将退出,

tail -f file | tr '[:lower:]' '[:upper:]' | head -n1

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

相关内容