为什么以下命令没有产生任何输出?
$ 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(至少在其终止之前没有)。
如果你找到第一个tail
pid 并杀死它,你应该然后查看第二个的输出。
答案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
永远不会收到通知文件末尾,因此可以永远不会发现这是它应该打印的最后几行。