配管到T型管时强制stdout进行线缓冲


117

通常,stdout是行缓冲的。换句话说,只要您的printf参数以换行符结尾,就可以期望该行会立即打印出来。使用管道重定向到时,似乎没有这种情况tee

我有一个C ++程序,a它将始终\n终止的字符串输出到stdout

当它自己运行(./a)时,所有内容均会按预期正确正确地打印。但是,如果将其通过管道传递到tee./a | tee output.txt),则在退出之前它不会打印任何内容,这违背了使用的目的tee

我知道我可以通过fflush(stdout)在C ++程序中的每次打印操作后添加一个来解决此问题。但是,有没有更清洁,更轻松的方法?例如,是否有我可以运行的命令stdout,即使使用管道,该命令也将被强制进行行缓冲?

Answers:


67

尝试将unbuffer其作为expect包装的一部分。您可能已经在系统上安装了它。

在您的情况下,您可以这样使用它:

./a | unbuffer -p tee output.txt

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


谢谢,这个工作,虽然我不得不编写expect自己作为unbuffer似乎并不在OS X中默认包含
houbysoft

@houbysoft:很高兴它为您服务。unbuffer只是一个小脚本,因此您不需要重新编译整个程序包。
暂停,直到另行通知。

是的,也许不是,但./configure && make花了大约10秒钟,然后我才搬进unbuffer/usr/local/bin:)
houbysoft

3
我通过brew将其安装在我的mac(10.8.5)上:brew install期望--with-brewed-tk
Nils 2013年

2
FWIW,因为unbuffer有点令人困惑,所以相关结构为unbuffer {commands with pipes/tee}
假名称

127

你可以试试 stdbuf

$ stdbuf -o 0 ./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.

您没有在stdbuf上运行tee,而是在上运行a,因此这不会影响您,除非您aa的源中设置的流的缓冲。

另外,stdbuf不是 POSIX,而是GNU-coreutils的一部分。


3
谢谢,但这在OS X上似乎不可用(问题标记为osx-lion)。
houbysoft

2
@houbysoft-我很确定GNU工具可以安装在OS X上
jordanm 2012年

1
@jordanm:也许吧,但是安装整个GNU工具似乎对此
有些

1
赞成这个答案,因为stdbuf我们正在使用的Centos Linux发行版中已经提供了该功能,而unbuffer现在没有。谢谢!
休·沃尔特斯

6
对于python脚本,stdbuf将不起作用,但是您可以使用-u禁用python一侧的缓冲:python3 -u a.py | tee output.txt
Honza

27

您还可以尝试使用命令在伪终端中执行script命令(这将强制对行进行行缓冲输出)!

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

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


3
script -t 1 /path/to/outputfile.txt ./a对于我的用例来说效果很好。它实时将所有输出流式传输到,outputfile.txt同时还将其打印到外壳的标准输出中。不需要使用tee
Peter Berg

26

您可以使用来自stdio.h的setlinebuf。

setlinebuf(stdout);

这应该将缓冲更改为“行缓冲”。

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


8
我不知道为什么这种解决方案没有这么多支持。这是唯一不给调用者带来负担的解决方案。
氧气

1
请注意,这不是标准C(甚至不是POSIX)。最好使用setvbuf(stdout, NULL, _IOLBF, 0),它是完全等效的。
rvighne '19

这解决了我在OS X Catalina上使用C ++程序的问题,该程序正在执行printf()ing,并且正在准备发球,但是在程序完成后才看到输出。
jbaxter

2

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


4
不幸的是,事实并非如此。即使使用std :: endl或std :: flush,您也可以使用c ++ std :: cout观察到相同的行为。缓冲发生在顶部,Linux中最简单的解决方案似乎是setlinebuf(stdout);。当您是程序的作者时,将其作为main()的第一行;当您无法更改源代码时,使用上述其他解决方案。
氧气

1
@oxygene这是不正确的。我试过了,当管道输送到tee时,endl会刷新缓冲区(与printf不同)。代码:#include <iostream> #include <unistd.h> int main(void) { std::cout << "1" << std::endl; sleep(1); std::cout << "2" << std::endl; }。endl始终按以下定义刷新缓冲区:en.cppreference.com/w/cpp/io/manip/endl
Curtis Yallop
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.