我使用gcc -lstdc++ main.cpp -o main.out
编译了以下C ++程序。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main(int argc, char** argv) {
cerr << "Error 1" << endl;
cout << "Ok " << endl;
cerr << "Wowza... that's bad..." << endl;
cerr << "Caused by X.";
cout << "All good in the end." << endl;
return 0;
};
我还有一个如下的bash脚本,其主要目的是为STDOUT添加&#34; SUCCESS:&#34;和STDERR与&#34;错误:&#34;。
./main.out > >(sed "s/^/SUCCESS: /g" >> main.log) 2> >(sed "s/^/ERROR : /g" >> main.log)
如果我cat main.log
,结果是:
ERROR : Error 1
ERROR : Wowza... that's bad...
ERROR : Caused by X.
SUCCESS: Ok
SUCCESS: All good in the end.
如您所见,发送到STDERR的字符串都出现在发送到STDOUT的字符串之前。
答案 0 :(得分:1)
令你烦恼的是glibc的行为。在Linux中,基于stdout和stderr被重定向的位置链接glibc的程序 - 几乎所有这些程序都是vary their output buffering mode。如果它们连接到TTY,则它们是行缓冲的。如果将它们重定向到文件,管道或其他非TTY设备,glibc会将它们切换到完全缓冲模式。在完全缓冲模式下,输出仅每4KB左右刷新一次。
在您的命令行中,这会影响main.out
和sed
。添加main.out
和>
重定向时,2>
的stdout和stderr会完全缓冲。两个sed
s'标准输出流完全缓冲,因为它们被重定向到main.log
。
您可以使用stdbuf
覆盖此行为。 stdbuf
运行一个带有您选择的输入和输出缓冲的命令。它适用于大多数程序。
如果为这三个命令中的每一个添加stdbuf
覆盖,则可以让它们交错输出。这是个好消息。
$ rm main.log; stdbuf -oL -eL ./main.out > >(stdbuf -oL sed "s/^/SUCCESS: /g" >> main.log) 2> >(stdbuf -oL sed "s/^/ERROR : /g" >> main.log); cat main.log
ERROR : Error 1
ERROR : Wowza... that's bad...
SUCCESS: Ok
SUCCESS: All good in the end.
ERROR : Caused by X.
$ rm main.log; stdbuf -oL -eL ./main.out > >(stdbuf -oL sed "s/^/SUCCESS: /g" >> main.log) 2> >(stdbuf -oL sed "s/^/ERROR : /g" >> main.log); cat main.log
SUCCESS: Ok
SUCCESS: All good in the end.
ERROR : Error 1
ERROR : Wowza... that's bad...
ERROR : Caused by X.
$ rm main.log; stdbuf -oL -eL ./main.out > >(stdbuf -oL sed "s/^/SUCCESS: /g" >> main.log) 2> >(stdbuf -oL sed "s/^/ERROR : /g" >> main.log); cat main.log
SUCCESS: Ok
ERROR : Error 1
SUCCESS: All good in the end.
ERROR : Wowza... that's bad...
ERROR : Caused by X.
坏消息是线路的顺序是不可预测的。仍然无法保证输出符合程序编写的顺序。
原因是,从根本上说,你有一个竞争条件。您的程序和两个sed
命令是三个独立的进程。没有办法保证它们会以某种顺序运行,当你的程序向stdout发出一行时,Linux会将控制切换到适当的sed
进程,然后切换回你的程序。
Linux可以允许程序写入其所有输出,然后将控制切换到sed
进程。它可以交错两个sed
进程。它可以执行上下文切换,但它喜欢。
更不用说,在多核或多处理器系统上,进程可以在完全相同的时间运行。这是一场真正的比赛。无论哪个sed
运行最快,都会先输出。
要同步处理,您必须摆脱多个sed
进程。相反,从两个流中读取一个进程。这是一个更复杂的设计。你需要两个输入描述符,而不仅仅是一个。你需要以某种方式select()
,只有在有可用输入的情况下才能从中读取。这种多路复用需要一些高级的shell脚本。你可能会用另一种语言做得更好。
答案 1 :(得分:-1)
我相信我可以通过演示自己回答1。
正如我所假设的那样,过程替换从右到左发生。
例如,执行./main.out 2> >(sed "s/^/ERROR : /g" >> main.log) > >(sed "s/^/SUCCESS: /g" >> main.log) ; cat main.log
会产生STDERR消息之前的所有STDOUT消息:
SUCCESS: Ok
SUCCESS: All good in the end.
ERROR : Error 1
ERROR : Wowza... that's bad...
ERROR : Caused by X.