为什么“tail -f ... | tail”无法产生任何输出?

tho*_*mie 37 pipe tail

为什么以下命令不产生任何输出?

$ tail -f /etc/passwd | tail
Run Code Online (Sandbox Code Playgroud)

阅读有关缓冲后,我尝试了以下无济于事:

$ tail -f /etc/passwd | stdbuf -oL tail
Run Code Online (Sandbox Code Playgroud)

请注意,以下确实会产生输出:

$ tail /etc/passwd | tail
Run Code Online (Sandbox Code Playgroud)

这样做也是如此:

$ tail -f /etc/passwd | head
Run Code Online (Sandbox Code Playgroud)

我使用的是尾部版本 8.21(GNU coreutils)。

Gha*_*ima 37

tail -f尾巴其实是现在不tail知道的东西,下一个怎么知道。另一方面tail -f,头部是已知的,因此可以进行处理。

或者更简单地说:tail相对于文件的末尾,但输出流tail -f没有 EOF(至少在其终止之前没有)。

如果您找到第一个tail的 pid 并杀死它,那么您应该看到第二个的输出。


sle*_*anc 21

技术解答

当以流作为输入运行时,tail保留一个n-line 缓冲区,它在读取流时填充该缓冲区,但在到达流末尾之前无法输出这些行,即EOF在尝试从输入读取时收到特殊代码溪流。调用tail -f不会退出,因此它永远不会关闭它的流,这使得不可能返回该流的最后 10 行。


bis*_*hop 17

tail显示最后 X 行。 tail -f做同样的事情,但本质上是一个无限循环:在启动时,显示文件的最后 X 行,然后使用一些操作系统魔法(如 inotify),监视并显示新行。

要完成它的工作,tail必须能够定位到文件的末尾。 如果tail找不到文件的结尾,则无法显示最后 X 行,因为“last”未定义。那么tail在这种情况下做什么呢?它一直等到它确实找到文件的结尾。

考虑一下:

$ chatter() { while :; do date; sleep 1; done; }
$ chatter | tail -f
Run Code Online (Sandbox Code Playgroud)

这似乎永远不会取得进展,因为从chatter.

如果您要求tail从文件系统管道中提供最后几行,您会得到相同的行为。考虑:

$ mkfifo test.pipe
$ tail test.pipe
Run Code Online (Sandbox Code Playgroud)

stdbuf绕过感知的问题是一种崇高的尝试。但关键的事实是,I/O 缓冲并不是根本原因:缺乏明确的文件结尾才是。如果您查看tail.c 源代码,您会看到file_lines 函数注释如下:

END_POS 是 EOF 的文件偏移量(比最后一个字节的偏移量大一个)。

这就是魔法。您需要一个文件结束符让 tail 在任何配置中工作。 head没有那个限制,它只需要一个文件开头(它可能没有,试试head test.pipe)。面向流的工具,如sedawk既不需要文件的开头或结尾:他们对缓冲区的工作。