可以在bash中同步进程重定向吗?

时间:2014-02-25 22:54:44

标签: c++ bash process

我使用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的字符串之前。

  1. 为什么会出现上述情况?例如,bash是否从右到左评估所有过程替换?
  2. 有没有办法同步这些,所以字符串的顺序是在C ++示例程序中定义的?

2 个答案:

答案 0 :(得分:1)

令你烦恼的是glibc的行为。在Linux中,基于stdout和stderr被重定向的位置链接glibc的程序 - 几乎所有这些程序都是vary their output buffering mode。如果它们连接到TTY,则它们是行缓冲的。如果将它们重定向到文件,管道或其他非TTY设备,glibc会将它们切换到完全缓冲模式。在完全缓冲模式下,输出仅每4KB左右刷新一次。

在您的命令行中,这会影响main.outsed。添加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.