管道到三通时强制线缓冲标准输出

hou*_*oft 103 unix buffer stdout pipe tee

通常,stdout是行缓冲的.换句话说,只要您的printf参数以换行符结尾,您就可以立即打印该行.使用管道重定向时,这似乎不成立tee.

我有一个C++程序,a它输出字符串,总是被\n终止stdout.

当它由它自己运行时(./a),所有内容都在正确的时间正确打印,正如预期的那样.但是,如果我将它传递给tee(./a | tee output.txt),它会在它退出之前不会打印任何内容,这会使使用的目的失效tee.

我知道我可以通过fflush(stdout)在C++程序中的每次打印操作之后添加一个来修复它.但是有更清洁,更简单的方法吗?例如,是否有一个命令可以运行stdout,即使使用管道也会强行进行行缓冲?

c00*_*ter 117

你可以试试 stdbuf

$ stdbuf -o 0 ./a | tee output.txt
Run Code Online (Sandbox Code Playgroud)

(大)手册页的一部分:

  -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.
Run Code Online (Sandbox Code Playgroud)

但要记住这一点:

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.
Run Code Online (Sandbox Code Playgroud)

你没在运行stdbuftee,你运行它a,所以这应该不会影响你,除非你设置的缓冲a的溪流a的源头".

另外,stdbuf不是 POSIX,但GNU-的coreutils的一部分.

  • 对于 python 脚本 stdbuf 将不起作用,但您可以使用 `-u` 禁用 python 端的缓冲:`python3 -u a.py | tee输出.txt` (14认同)
  • 谢谢,但这似乎不适用于OS X(问题标记为osx-lion). (3认同)
  • 赞成这个答案是因为 `stdbuf` 已经在我们使用的 Centos Linux 发行版上可用,而 `unbuffer` 不是。谢谢! (3认同)
  • @houbysoft-我很确定GNU工具可以安装在OS X上 (2认同)
  • @jordanm:也许吧,但是安装整个 GNU 工具似乎有点过分... (2认同)

Pau*_*ce. 53

尝试unbuffer哪个是expect包的一部分.您可能已经在系统上拥有它.

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

./a | unbuffer -p tee output.txt

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

  • 我把它安装在我的mac(10.8.5)上通过brew:brew install expect --with-brewed-tk (2认同)
  • FWIW,因为 unbuffer 有点混乱,相关的结构是`unbuffer {commands with pipe/tee}`。 (2认同)

小智 26

您也可以尝试使用命令在伪终端中执行script命令(这应该强制执行到管道的行缓冲输出)!

script -q /dev/null ./a | tee output.txt     # Mac OS X, FreeBSD
script -c "./a" /dev/null | tee output.txt   # Linux
Run Code Online (Sandbox Code Playgroud)

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

  • `script -t 1 /path/to/outputfile.txt ./a`对我的用例非常有用.它将所有输出实时流输出到`outputfile.txt`,同时将其打印到shell的标准输出.不需要使用`tee` (3认同)

小智 21

您可以使用stdio.h中的setlinebuf.

setlinebuf(stdout);
Run Code Online (Sandbox Code Playgroud)

这应该将缓冲更改为"行缓冲".

如果您需要更多灵活性,可以使用setvbuf.

  • 我想知道为什么这个解决方案有这么少的赞成票.这是唯一不给呼叫者带来负担的解决方案. (8认同)
  • 请注意,这不是标准 C(甚至不是 POSIX)。最好使用“setvbuf(stdout, NULL, _IOLBF, 0)”,它是完全等价的。 (4认同)

use*_*ser 9

@Paused 直到进一步通知答案中的包unbuffer中的命令对我来说不起作用,因为它呈现的方式。expect

而不是使用:

./a | unbuffer -p tee output.txt

我不得不使用:

unbuffer -p ./a | tee output.txt

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

expect软件包可以安装在:

  1. MSYS2 与pacman -S expect
  2. Mac 操作系统brew install expect

更新

我最近在 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缓冲输出的问题。