在管道中强制标准输出的行缓冲
Posted
技术标签:
【中文标题】在管道中强制标准输出的行缓冲【英文标题】:Force line-buffering of stdout in a pipeline 【发布时间】:2012-07-05 10:15:17 【问题描述】:通常,stdout
是行缓冲的。换句话说,只要您的 printf
参数以换行符结尾,您就可以期望该行会立即打印。当使用管道重定向到 tee
时,这似乎不成立。
我有一个 C++ 程序a
,它输出字符串,总是\n
-终止,到stdout
。
当它自行运行 (./a
) 时,一切都按预期在正确的时间正确打印。但是,如果我将它通过管道传输到 tee
(./a | tee output.txt
),它在退出之前不会打印任何内容,这违背了使用 tee
的目的。
我知道我可以通过在 C++ 程序中的每次打印操作后添加 fflush(stdout)
来修复它。但是有没有更清洁、更简单的方法?有没有我可以运行的命令,例如,它会强制 stdout
进行行缓冲,即使在使用管道时也是如此?
【问题讨论】:
【参考方案1】:你可以试试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 的一部分。
【讨论】:
谢谢,但这似乎在 OS X 上不可用(问题标记为 osx-lion)。 @houbysoft - 我很确定 GNU 工具可以安装在 OS X 上 @jordanm:也许吧,但是安装整个 GNU 工具似乎有点矫枉过正...... 赞成这个答案,因为stdbuf
已经在我们使用的 Centos Linux 发行版上可用,而 unbuffer
不可用。谢谢!
对于 python 脚本 stdbuf 将不起作用,但您可以使用 -u
禁用 python 端的缓冲:python3 -u a.py | tee output.txt
【参考方案2】:
试试unbuffer
,它是expect
包的一部分。您可能已经在您的系统上安装了它。
在你的情况下,你会这样使用它:
./a | unbuffer -p tee output.txt
(-p
用于管道模式,其中 unbuffer 从标准输入读取并将其传递给其余参数中的命令)
【讨论】:
谢谢,这行得通,虽然我必须自己编译expect
,因为unbuffer
似乎默认不包含在 OS X 中。
我通过 brew 将它安装在我的 mac (10.8.5) 上:brew install expect --with-brewed-tk
FWIW,因为unbuffer有点混乱,相关结构是unbuffer commands with pipes/tee
。
我认为它应该是“unbuffer ./a | tee output.txt”——它不是需要取消缓冲的 tee。至少,对于类似的问题,这对我有用。
如果您阅读手册页,我认为正确的命令是unbuffer ./a | tee output.txt
。这就是在 RPi 下使用 bash 和 tmux 对我有用的方法。【参考方案3】:
您也可以尝试使用script
命令在伪终端中执行您的命令(这应该强制将行缓冲输出到管道)!
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 的标准输出。不需要使用tee
script
from util-linux 和 BSD 都提供了-e
选项来返回执行命令的退出状态。【参考方案4】:
您可以使用 stdio.h 中的 setlinebuf。
setlinebuf(stdout);
这应该将缓冲更改为“行缓冲”。
如果您需要更大的灵活性,可以使用 setvbuf。
【讨论】:
我想知道为什么这个解决方案的赞成票这么少。这是唯一不会给调用者带来负担的解决方案。 请注意,这不是标准的 C(甚至是 POSIX)。使用setvbuf(stdout, NULL, _IOLBF, 0)
可能会更好,这完全是等价的。
这解决了我在 OS X Catalina 上的问题,它使用了一个 C++ 程序,该程序正在 printf()ing 并且我正在管道到 tee 但仅在程序完成后才看到输出。【参考方案5】:
@Paused until further notice 答案中的expect
包中的unbuffer
命令对我来说并不像它呈现的方式那样工作。
而不是使用:
./a | unbuffer -p tee output.txt
我不得不使用:
unbuffer -p ./a | tee output.txt
(
-p
用于管道模式,其中 unbuffer 从标准输入读取并将其传递给其余参数中的命令)
expect
包可以安装在:
-
MSYS2 与
pacman -S expect
带有brew install expect
的Mac OS
更新
我最近在 shell 脚本中遇到了 python
的缓冲问题(尝试将时间戳附加到其输出时)。解决方法是通过这种方式将-u
标志传递给python
:
run.sh
与 python -u script.py
unbuffer -p /bin/bash run.sh 2>&1 | tee /dev/tty | ts '[%Y-%m-%d %H:%M:%S]' >> somefile.txt
此命令将在输出中添加时间戳,并同时将其发送到文件和标准输出。
ts
程序(时间戳)可以与moreutils
包一起安装。
更新 2
最近,grep
缓冲输出也有问题,当我在 grep
上使用参数 grep --line-buffered
来停止缓冲输出时。
【讨论】:
我也遇到了同样的情况。这样,它就奏效了。【参考方案6】:如果您改用 C++ 流类,则每个 std::endl
都是 隐式刷新。使用C风格的打印,我认为你建议的方法(fflush()
)是唯一的方法。
【讨论】:
不幸的是,这不是真的。即使在使用 std::endl 或 std::flush 时,您也可以使用 c++ std::cout 观察到相同的行为。缓冲发生在顶部,Linux 中最简单的解决方案似乎是 setlinebuf(stdout);当您是程序的作者并在无法更改源代码时使用上述其他解决方案时,作为 main() 的第一行。 @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以上是关于在管道中强制标准输出的行缓冲的主要内容,如果未能解决你的问题,请参考以下文章