Force line-buffering of stdout when piping to tee
通常,stdout是线路缓冲的。换句话说,只要您的printf参数以换行符结束,您就可以期望该行立即打印。当使用管道重定向到tee时,这似乎不适用。
我有一个C++程序,EDOCX1,3,输出字符串,总是EDCOX1,4终止,到EDCOX1×0。
当它自己运行(./a)时,一切都按预期在正确的时间正确打印。但是,如果我把它放到tee(./a | tee output.txt上),它在退出之前不会打印任何内容,这就破坏了使用tee的目的。
我知道,在C++程序中的每个打印操作之后,我可以通过添加一个EDOCX1 10来修复它。但是有没有一种更清洁、更简单的方法?例如,我是否可以运行一个命令,即使在使用管道时,也会强制stdout进行行缓冲?
你可以试试stdbuf。
1
| $ stdbuf -o 0 ./a | tee output.txt |
(大)手册的一部分:
1 2 3 4 5 6 7 8 9 10 11 12 13
| -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. |
不过,请记住:
1 2 3 4
| 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的一部分。
- 谢谢,但这似乎在OSX上不可用(问题被标记为OSX Lion)。
- @HoubySoft-我很确定GNU工具可以安装在OSX上
- @乔丹姆:也许吧,但是安装整个GNU工具似乎有点过分了……
- 这个答案被否决了,因为我们使用的CentOS Linux发行版已经提供了stdbuf,而unbuffer没有。谢谢!
尝试unbuffer,它是expect包的一部分。您的系统中可能已经有了它。
在您的情况下,您可以这样使用它:
./a | unbuffer -p tee output.txt
(-p用于管道模式,其中unbuffer从stdin读取数据并在其余参数中将其传递给命令)
- 谢谢,这是有效的,尽管我不得不自己编译expect,因为unbuffer在OSX中似乎没有默认包含。
- @Houbysoft:我很高兴它对你有用。unbuffer只是一个小脚本,因此不需要重新编译整个包。
- 是的,可能不是,但是./configure && make花了大约10秒,然后我把unbuffer移到了/usr/local/bin。
- 很高兴知道这一点,我以前没见过unbuffer;)
- 我通过BREW在我的Mac(10.8.5)上安装了它:BREW安装Expect--使用BREW Tk
- fwiw,因为unbuffer有些混乱,所以相关的结构是unbuffer {commands with pipes/tee}。
- 我必须这样安装:brew install homebrew/dupes/expect
- 当程序需要在命令行上输入时,这对我来说有问题:它不打印提示(除非我按Enter键,然后输入被接受为提示的输入,这样做太晚了)。但是,stdbuf起作用了。
您还可以尝试使用script命令(它应该强制执行到管道的行缓冲输出)在伪终端中执行命令!
1 2
| script -q /dev/null ./a | tee output.txt # Mac OS X, FreeBSD
script -c"./a" /dev/null | tee output.txt # Linux |
请注意,script命令不会传播包装命令的退出状态。
- script -t 1 /path/to/outputfile.txt ./a对我的用例很有用。它将所有输出流实时输出到outputfile.txt,同时将其打印到shell的stdout。不需要使用tee。
您可以使用stdio.h中的setlinebuf。
这应该将缓冲改为"线路缓冲"。
如果您需要更多的灵活性,可以使用setvbuf。
- 我想知道为什么这个解决方案没有多少赞成票。这是唯一一个不给主叫方带来负担的解决方案。
如果您使用C++流类,则每个EDCOX1(11)都是隐式刷新。使用C型打印,我认为您建议的方法(fflush()是唯一的方法。
- 不幸的是,这不是真的。即使使用STD::Endl或STD::FLUH,也可以观察到与C++ STD::COUT相同的行为。缓冲发生在最上面,Linux中最简单的解决方案似乎是setLineBuf(stdout);当您是程序的作者时,作为main()中的第一行,当您无法更改源代码时,使用上面的其他解决方案。
- @氧基因这不是真的。我试过了,当管道连接到三通时,endl会冲洗缓冲器(与printf不同)。代码:#include #include int main(void) { std::cout <<"1" << std::endl; sleep(1); std::cout <<"2" << std::endl; }。endl始终按照此处定义刷新缓冲区:en.cppreference.com/w/cpp/io/manip/endl