2014-02-25 33 views
2

我有用gcc -lstdc++ main.cpp -o main.out編譯的以下C++程序。可以在bash中處理重定向嗎?

#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「成功」和STDERR與「ERROR」。

./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所有的字符串發送到標準輸出字符串之前出現。

  1. 爲什麼會出現上述情況?例如,bash是否評估從右到左的所有過程替換?
  2. 有沒有什麼方法可以同步這些,這樣字符串的順序就是C++示例程序中定義的順序?

回答

1

多數民衆贊成傷腦筋,你是的glibc的行爲。在Linux中,與glibc —鏈接的程序幾乎全部都是— vary their output buffering mode,這些程序基於stdout和stderr被重定向的位置。如果他們連接到TTY,他們是線路緩衝。如果將它們重定向到文件,管道或其他非TTY設備,則glibc將它們切換到完全緩衝模式。在完全緩衝模式下,輸出每4KB左右刷新一次。

在您的命令行中,這會同時影響main.outsed。當您添加>2>重定向時,main.out的stdout和stderr會被完全緩衝。兩個sed的標準輸出流被完全緩衝,因爲它們被重定向到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命令是三個獨立的進程。沒有辦法保證它們按照特定順序運行,當程序吐出一條線到標準輸出Linux時,控制器將切換到合適的sed進程,然後切換回您的程序。

Linux可能允許您的程序寫入其所有輸出,然後將控制切換到sed進程。它可以交錯兩個sed進程。它可以執行上下文切換,但它喜歡。

更不用說,在多核或多處理器系統上,進程可以同時運行。這是一場真正的比賽。無論哪個sed運行速度最快都會先輸出。

同步處理您必須擺脫多個sed進程。相反,從兩個流中讀取一個進程。這是一個更復雜的設計。你需要兩個輸入描述符,而不僅僅是一個。您需要以某種方式select(),並且只有在有可用輸入的情況下才能從中讀取。這種多路複用需要一些高級的shell腳本。你可能會更好地用另一種語言來做這件事。

+0

壞消息是行的交錯是隨機的(如你的三個例子用相同的命令,但輸出不同)? – magnus

+0

@ user1420752是的。有效隨機。如果您想讓我詳細說明我的最後一段,請告訴我。這需要我花一些時間來爲此舉一個例子。 –

+0

我可以說......你是一個傳奇人物。我認爲自己在Linux方面非常熟練,但你的答案是現成的。我希望有一種更簡單的方法來實現我想要的功能,例如,如果bash具有使用'>()'語法在進程間交替的功能。我也試過'rm main.log; ./main.out>>(sed「s/^/$(date +%s。%N)SUCCESS:/ g」>> main.log)2>>(sed「s/^/$(date + s。%N)錯誤:/ g「>> main.log);貓main.log;同步 ; sort -nk1 main.log'但程序執行速度如此之快(或者定時器分辨率非常不準確),以至於每個進程重定向只有一個時間值。 – magnus

-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.