4

当我在 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

4 回答 4

6

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

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

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

也可以看看:

关闭管道中的缓冲

于 2013-05-29T20:49:56.373 回答
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

于 2013-05-29T20:56:55.943 回答
3

首先,使用colortail,colorize或之类的日志着色工具ccze

在更一般的情况下,问题在于当 stdout 不是终端时,libc 会通过缓冲来自动优化吞吐量。这可以显着减少非交互式作业的系统调用数量。对于这个以及更多,比较交互式和非交互式输出

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

Wumpus 的方法,用于更改缓冲的解释器指令:

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

程序特定设置:

  • -u在 Python 和 GNU 中
  • --line-buffered在 GNU awk 中

为终端打开 PTY,使 stdout 成为 TTY 并规避问题:

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

最后$LD_PRELOAD,将选项注入进程的 C 运行时的 hack(巧妙地包装在 GNU coreutilsstdbuf中,由 Vilhelm 建议)

于 2013-05-29T21:33:53.540 回答
2

您可以将几行 perl 代码与重复的 -e 选项连接起来(确保它们以;-- 它们被串在一起形成一个程序)。你可以用$|=1. 有关详细信息,请参阅perl 手册$|(页面下方 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 行。但是如果你继续管道到另一个程序,你会想要它。

于 2013-05-29T21:18:51.013 回答