如何防止管道延迟输出?

时间:2013-05-29 20:39:19

标签: bash

当我在bash中运行以下命令时,它会等到程序完成后再刷新所有输出。如果我移除管道,则立即打印每一行。

{ for i in `seq 3` ; do echo $i ; sleep 1 ; done ; } \
    | perl -p -e 's,(.*ERROR.*),\e[01;31m\1\e[00m,g' \
    | perl -p -e 's,(.*WARNING.*),\e[01;33m\1\e[00m,g' \
    | perl -p -e 's,(.*TCPEchoTest.*),\e[01;30m\1\e[00m,g' \
    | perl -p -e 's,(.*enters.*),\e[00;33m\1\e[00m,g'

如何使用管道并且仍然立即打印每条线?

4 个答案:

答案 0 :(得分:4)

管道通常具有默认的4kB缓冲区,其中输出可以保持直到程序完成执行。

尝试使用stdbuf实用程序禁用此缓冲区,以便立即输出文本:

stdbuf -i0 -o0 -e0 command | perl ...

另见:

  

Turn off buffering in pipe

答案 1 :(得分:4)

perl正在执行缓冲,因此perl命令是您需要更改以将其关闭的命令。 在每个perl命令中设置$|变量,如下所示:

{ for i in `seq 3` ; do echo $i ; sleep 1 ; done ; } \
    | perl -p -e 'BEGIN{$|=1}s,(.*ERROR.*),\e[01;31m\1\e[00m,g' \
    | perl -p -e 'BEGIN{$|=1}s,(.*WARNING.*),\e[01;33m\1\e[00m,g' \
    | perl -p -e 'BEGIN{$|=1}s,(.*TCPEchoTest.*),\e[01;30m\1\e[00m,g' \
    | perl -p -e 'BEGIN{$|=1}s,(.*enters.*),\e[00;33m\1\e[00m,g'

BEGIN块导致分配在读取任何输入之前发生一次,而不是在使用-p读取每一行之后执行此操作。

答案 2 :(得分:3)

首先,使用colortailcolorizeccze等日志着色工具。

在更一般的情况下,问题是当stdout不是终端时,libc将通过缓冲自动优化吞吐量。这可以显着减少非交互式作业的系统调用次数。对于此以及更多内容,请比较interactive and non-interactive output

当进程认为它不是交互式的时,虽然你知道它是,但有几种方法可以说服它在每次逻辑写入后刷新缓冲区:

Wumpus的方法,解释改变缓冲的说明:

  • $|=1或Perl中的等效STDOUT->autoflush(1)
  • 写完awk后
  • fflush()
  • 用Python写作后
  • sys.stdout.flush()

计划特定设置:

    Python中的
  • -u和GNU sed
  • GNU awk中的
  • --line-buffered

打开终端的PTY,使stdout成为TTY并解决问题:

  • script -q /dev/null yourcommand
  • 来自unbuffer
  • expect package

最后$LD_PRELOAD hacks将选项注入进程的C运行时(整齐地包装在GNU coreutils stdbuf中,由Vilhelm建议)

答案 3 :(得分:2)

你可以用重复的-e选项连接几行perl代码(一定要用;结束它们 - 它们串在一起形成一个程序)。你可以使用$|=1使管道“热”。有关详细信息,请参阅perl manual on $|(页面下方2/3,搜索OUTPUT_AUTOFLUSH)。

{ for i in `seq 3` ; do echo $i ; sleep 1 ; done ; } \
  | perl -p -e 'BEGIN{$|=1};' \
            -e 's,(.*ERROR.*),\e[01;31m\1\e[00m,g;' \
            -e 's,(.*WARNING.*),\e[01;33m\1\e[00m,g;' \
            -e 's,(.*TCPEchoTest.*),\e[01;30m\1\e[00m,g;' \
            -e 's,(.*enters.*),\e[00;33m\1\e[00m,g;'

打印1,2,3,每个数字之间有一秒钟。实际上,当perl输出到终端时,不需要BEGIN行。但是如果你继续管道到另一个程序,你想要它。