鉴于这个最小的例子
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; )
Run Code Online (Sandbox Code Playgroud)
它输出LINE 1,然后,一秒钟后LINE 2,如预期的那样输出。
如果我们通过管道将其传输到 grep LINE
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,行为与前一种情况相同。
如果,或者,我们通过管道将其传输到 cat
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | cat
Run Code Online (Sandbox Code Playgroud)
行为再次与预期相同。
但是,如果我们通过管道连接到grep LINE,然后到cat,
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep LINE | cat
Run Code Online (Sandbox Code Playgroud)
一秒钟过去之前没有输出,并且两行立即出现在输出上,这是我没想到的。
为什么会发生这种情况,如何使最后一个版本的行为与前三个命令相同?
Ste*_*itt 39
当(至少 GNU)grep的输出不是终端时,它会缓冲其输出,这就是导致您看到的行为的原因。您可以使用 GNUgrep的--line-buffered选项禁用此功能:
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | grep --line-buffered LINE | cat
Run Code Online (Sandbox Code Playgroud)
或stdbuf实用程序:
( echo "LINE 1" ; sleep 1 ; echo "LINE 2" ; ) | stdbuf -oL grep LINE | cat
Run Code Online (Sandbox Code Playgroud)
Jde*_*eBP 26
像许多实用程序一样,这不是某个程序所特有的,grep它的标准输出在行缓冲和完全缓冲之间变化。在前一种情况下,C 库将输出数据缓存在内存中,直到保存这些数据的缓冲区被填满或向其中添加换行符(或程序干净地结束),然后它调用write()实际写入缓冲区内容。在后一种情况下,只有内存缓冲区变满(或程序干净地结束)才会触发write().
这是众所周知但略有错误的解释。事实上,标准输出不是行缓冲,而是在 GNU C 库和 BSD C 库中智能缓冲。当读取标准输入耗尽其内存缓冲区(预读输入)并且 C 库必须调用以获取更多输入并且它正在读取新行的开头时,标准输出也会被刷新。(这样做的一个原因是当另一个程序将自身连接到过滤器的两端并期望能够逐行操作,在写入过滤器和读取过滤器之间交替时防止死锁;就像 GNU 中的“协进程”例如。)read()awk
grep而其他实用程序会这样做——或者更严格地说,它们使用的 C 库会这样做,因为这是 C 语言编程的一个定义特性——基于它们检测到的标准输出是什么。如果(且仅当)它不是交互式设备,则他们选择全缓冲,否则他们选择智能缓冲。管道被认为不是交互式设备,因为作为交互式设备的定义,至少在 Unix 和 Linux 的世界中,本质上是isatty()对相关文件描述符返回 true的调用。
一些实用程序grep具有特殊的选项,例如--line-buffered更改此决定的选项,如您所见,这是错误的命名。但是实际上可以使用的过滤器程序中只有极少一部分具有这样的选项。
更一般地说,人们可以使用深入研究 C 库特定内部结构并更改其决策的工具(如果要更改的程序是 set-UID,则存在安全问题,并且也特定于特定的 C 库,并且确实是具体到写在或铺在C语言的上层程序),或工具,如ptybandage认为不改变程序的内部结构,而只是介伪终端作为标准输出,以便决策出来的“互动”,以影响这个。