管道中标准输出的强制线路缓冲

通常,stdout是行缓冲的。换句话说,只要 printf参数以换行结束,就可以立即打印该行。当使用管道重定向到 tee时,这种情况似乎不存在。

我有一个 C + + 程序 a,它将字符串(始终以 \n结尾)输出到 stdout

当它自己运行时(./a) ,一切都按照预期在正确的时间正确地打印。但是,如果我将它导入到 tee(./a | tee output.txt) ,它在退出之前不会打印任何内容,这违背了使用 tee的目的。

我知道我可以通过在 C + + 程序中的每次打印操作之后添加一个 fflush(stdout)来修复它。但是有没有更干净,更简单的方法呢?有没有一个命令我可以运行,例如,将强制 stdout行缓冲,即使使用管道?

72855 次浏览

你可以试试 stdbuf

$ stdbuf --output=L ./a | tee output.txt

(大)手册页的一部分:

  -i, --input=MODE   adjust standard input stream buffering
-o, --output=MODE  adjust standard output stream buffering
-e, --error=MODE   adjust standard error stream buffering


If MODE is 'L' the corresponding stream will be line buffered.
This option is invalid with standard input.


If MODE is '0' the corresponding stream will be unbuffered.


Otherwise MODE is a number which may be followed by one of the following:
KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.
In this case the corresponding stream will be fully buffered with the buffer
size set to MODE bytes.

不过,请记住这一点:

NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does
for e.g.) then that will override corresponding settings changed by 'stdbuf'.
Also some filters (like 'dd' and 'cat' etc.) dont use streams for I/O,
and are thus unaffected by 'stdbuf' settings.

您不是在 tee上运行 stdbuf,而是在 a上运行它,所以这应该不会影响您,除非您在 a的源中设置了 a流的缓冲区。

而且,stdbuf没有 POSIX,但是是 GNU-coreutils 的一部分。

试试 unbuffer(man) ,它是 expect软件包的一部分。你可能已经把它放在你的系统上了。

在你的情况下,你会这样使用它:

unbuffer ./a | tee output.txt

-p选项用于管道模式,其中 unbuffer 从 stdin 读取并将其传递给其余参数中的命令。

如果改用 C + + 流类,则每个 std::endl 都是隐式刷新。使用 C 风格打印,我认为你建议的方法(fflush())是唯一的方法。

您还可以尝试使用 script命令在伪终端中执行命令(该命令应该对管道执行行缓冲输出) !

script -q /dev/null ./a | tee output.txt     # Mac OS X, FreeBSD
script -c "./a" /dev/null | tee output.txt   # Linux

请注意,script命令不会传播回已包装命令的退出状态。

您可以从 stdio.h 使用 setlinebuf。

setlinebuf(stdout);

这将把缓冲更改为“行缓冲”。

如果需要更大的灵活性,可以使用 setvbuf。

@ 暂停,等待进一步通知答案的 expect软件包中的 unbuffer命令对我来说并不像它所呈现的那样有效。

不使用:

./a | unbuffer -p tee output.txt

我不得不用:

unbuffer -p ./a | tee output.txt

(-p用于管道模式,其中 unbuffer 从 stdin 读取并将其传递给其余参数中的命令)

expect软件包可以安装在:

  1. MSYS2和 pacman -S expect
  2. brew install expect的 Mac OS

更新

我最近在一个 shell 脚本中遇到了 python的缓冲问题(当试图将时间戳追加到它的输出时)。解决办法是以这种方式将 -u标志传递给 python:

  1. run.shpython -u script.py
  2. unbuffer -p /bin/bash run.sh 2>&1 | tee /dev/tty | ts '[%Y-%m-%d %H:%M:%S]' >> somefile.txt
  3. 这个命令将在输出上放置一个时间戳,并同时将其发送到文件和标准输出。
  4. ts程序(时间戳)可以与 moreutils包一起安装。

更新2

最近,当我使用 grep上的参数 grep --line-buffered来停止缓冲输出时,grep也出现了缓冲输出的问题。

IMO 的最佳答案是 grep--line-buffer选项,如下所述:

Https://unix.stackexchange.com/a/53445/40003