通过两个命令管道 ping 后没有打印

Nothing prints after piping ping through two commands

运行 这个:

ping google.com | grep -o 'PING'

将打印 PING 到终端,所以我假设这意味着 grep 的标准输出被终端捕获。

那么为什么 follow 命令不打印任何东西?终端刚刚挂起:

ping google.com | grep -o 'PING' | grep -o 'IN'

我认为第一个 grep 命令的标准输出将被重定向到第二个 grep 命令的标准输入。然后第二个 grep 的标准输出将被终端捕获并打印。

这似乎是将 ping 替换为 echo 时会发生的情况:

echo 'PING' | grep -o 'PING' | grep -o 'IN'

IN 打印到终端,如我所料。

那么 ping 有什么特别之处可以防止打印任何内容?

你可以试着更有耐心:-)

ping google.com | grep -o 'PING' | grep -o 'IN'

最终显示输出,但可能需要半小时左右。

在 Unix 下,如果流是终端,则程序启动时传递给程序的标准输出流是 "line-buffered";否则它是完全缓冲的,通常有 8 KB(8,192 个字符)的缓冲区。缓冲意味着输出在内存中累积,直到缓冲区已满,或者在行缓冲流的情况下,直到发送换行符。

当然,程序可以覆盖此设置,而只产生少量输出的程序(如 ping)通常会使 stdout 行缓冲,而不管它是什么。但是 grep 不会这样做(尽管您可以使用 --line-buffered 命令行选项告诉 Gnu grep 这样做。)

"Pipes"(为实现 | 运算符而创建)不被视为终端。所以中间的 grep 将有一个完全缓冲的输出,这意味着它的输出将被缓冲直到写入 8k 个字符。在您的情况下,这将花费一些时间,因为每行仅包含五个字符(PING 加上一个换行符),并且它们每行生成一次。所以缓冲区将在大约 1640 秒后填满,这将近 28 分钟。

许多 unix 发行版都带有一个名为 stdbuf 的程序,可用于在 运行 程序之前更改标准流的缓冲。 (如果您有 stdbuf,您可以通过键入 man 1 stdbuf 来了解它是如何工作的。)像 Perl 这样的编程语言通常提供其他机制来调用 stdbuf 标准库函数。 (在 Perl 中,您可以在每次写入后使用内置变量 $|autoflush(BOOL) io 句柄方法强制刷新。)

当然,当程序成功终止时,所有输出缓冲区都是"flushed"(srnt到它们各自的流)。所以

echo PING | grep -o 'PING' | grep -o 'IN'

会立即输出它唯一的输出行。但是 ping 不会终止,除非您提供计数命令行选项(-c N;请参阅 man ping)。因此,如果您需要即时的管道吞吐量,您可能需要修改缓冲行为。