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

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

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

$ tail -f /etc/passwd | tail

阅读完有关缓冲,我尝试了以下方法,但没有成功:

$ tail -f /etc/passwd | stdbuf -oL tail

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

$ tail /etc/passwd | tail

这也是如此:

$ tail -f /etc/passwd | head

我正在使用 tail 版本 8.21 (GNU coreutils)。

答案1

tail -f尾巴其实是当下未知的东西,下一个又该如何tail知道呢。另一方面,tail -f头部是已知的并且因此可以被处理。

或者说得更简单:tail是相对于文件结束而言的,但是输出流tail -f没有EOF(至少在其终止之前没有)。

如果你找到第一个tailpid 并杀死它,你应该然后查看第二个的输出。

答案2

技术解答

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

答案3

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

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

考虑一下:

$ chatter() { while :; do date; sleep 1; done; }
$ chatter | tail -f

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

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

$ mkfifo test.pipe
$ tail test.pipe

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

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

这就是魔力。您需要一个文件结尾才能让 tail 在任何配置中工作。 head没有这个限制,它只需要一个文件的开头(它可能没有,尝试head test.pipe)。面向流的工具,例如sed和 ,awk不需要文件的开头或结尾:它们在缓冲区上工作。

答案4

的功能tail是显示输入或文件的最后一部分 - “尾部”。 (该选项-f是关于它稍后执行的操作,因此与此处无关。)

让我们考虑一个文件:

是什么文件的最后一部分
假设这是最后n行一个文件的。

当我们读取i输入文件的行时,如何决定是否需要打印它?
我们不知道它是否在最后一部分 - 因为我们不知道最后一行将是哪一行。所以我们不能立即打印。

我们需要保持线路直到清楚这是最后n几行的一部分,或者不再是最后几行的一部分,因为我们知道n更多的行

如果我们现在来到文件末尾,我们知道n我们保留的最后几行实际上是n文件的最后几行。

现在,在这样的情况下

tail -f /etc/passwd | tail

首先tail读取文件,然后等待获取更多数据从中也把它写出来。所以它会不发出文件结束信号当谈到它读取的文件末尾时,到第二个尾部。没有这个,第二个tail 永远不会收到通知文件末尾,因此可以永远不会发现这是它应该打印的最后几行。

相关内容