Force grep Line Buffered

有时候我们需要在 shell 中执行类似 some-cmd | grep keyword | tee filtered.log 的命令,如果管道的起始命令输出的东西不够多,那么 grep 会攒够一定长度的输出才进行 flush 操作。这个行为会导致若提前使用 ^C 来结束管道,则会丢失很大一部分本应过滤出来的内容。

一个麻烦但是可行的解决方法是只结束管道的起点进程,如找到上述的 some-cmd 进程的 pid 并用 kill 发送 TERM 或其他信号使之退出,这样后面的 grep 在检测到输入流被关闭后会进行 flush 操作,将缓冲区的数据刷新到标准输出。

实际上 grep 提供了一个参数来设置它的输出流缓冲模式,即 --line-buffered ,该选项会将输出流设置成行缓冲,这在我们处理日志时十分有用,因为管道的源头一般是按行为单位产生日志的,这样如果 grep 也是行缓冲则在使用 ^C 结束整个管道时也并不会丢失内容。这样上述的命令就变成了这样:

1
some-cmd | grep --line-buffered | tee filtered.log

需要注意的是,如果管道中有多个 grep 命令,那么它们都需要设置该参数来确保每一行都会被 flush 到输出流;可以设置一个 shell alias 来简化该操作。这种方法的缺点是降低了 grep 的吞吐量,不过在大部分的调试场景应该是够用的了。