2010-12-21 191 views
9

出於測試目的,我想保存stdout和單獨標準錯誤通過後續的代碼檢查。例如,錯誤輸入的測試運行應該導致輸出到標準錯誤,但沒有任何標準輸出,而測試運行正確的輸入應導致輸出到標準輸出,但沒有標準錯誤。節省必須是同步的,以避免與測試競爭條件(所以我不能使用process substitution)。保存標準輸出,標準錯誤和標準輸出+標準錯誤同步

爲了能夠在事後調試測試,我需要看到stdout和stderr在它們輸出的順序。因此,我必須將它們保存到相同的文件/變量/任何內容,或者將它們同時保存到終端。

要測試發生了哪個錯誤,我還需要命令的退出代碼

由於效率和準確性的原因,我當然不能每次運行兩次測試。

它是例如可能重定向標準輸出到stdout.log,stderr到stderr.log,他們都在同一個命令output.log?或者對stdout和stderr分別使用同步tee命令?或者保存stdout和stderr的副本來分離變量?

更新:它看起來像Tim的解決方案几乎作品(修改爲輸出端上,而不是記錄到all.log):

$ set -o pipefail 
$ { 
    { 
     echo foo | tee stdout.log 2>&3 3>&- 
    } 2>&1 >&4 4>&- | tee stderr.log 2>&3 3>&- 
} 3>&2 4>&1 
foo 
$ cat stdout.log 
foo 
$ cat stderr.log 
$ { 
    { 
     echo foo >&2 | tee stdout.log 2>&3 3>&- 
    } 2>&1 >&4 4>&- | tee stderr.log 2>&3 3>&- 
} 3>&2 4>&1 
foo 
$ cat stdout.log 
$ cat stderr.log 
foo 
$ bar=$({ 
    { 
     echo foo | tee stdout.log 2>&3 3>&- 
    } 2>&1 >&4 4>&- | tee stderr.log 2>&3 3>&- 
} 3>&2 4>&1) 
$ echo "$bar" 
foo 
$ cat stdout.log 
foo 
$ cat stderr.log 
$ bar=$({ 
    { 
     echo foo >&2 | tee stdout.log 2>&3 3>&- 
    } 2>&1 >&4 4>&- | tee stderr.log 2>&3 3>&- 
} 3>&2 4>&1) 
$ cat stdout.log 
$ cat stderr.log 
foo 
$ echo "$bar" 
foo 

這似乎除了最後一次迭代,其中值工作的bar被設置爲stderr的內容。任何建議所有這些工作?

回答

11

請參閱BashFAQ/106。吃蛋糕並吃它也不容易。

在這個頁面:

但是有些人不會接受輸出和錯誤,或者行不同步之間的分離或者損失。他們是純粹主義者,所以他們要求所有最難的形式 - 我想將stdout和stderr一起記錄到一個文件中,但我也希望他們保持原來的單獨目的地。

爲了做到這一點,我們首先要提出幾點注意事項:

  • 如果有將是兩個獨立的輸出和錯誤流,那麼一些過程中有寫他們每個人。
  • 由於shell沒有poll(2)或select(2)接口,因此無法在shell腳本中編寫一個進程,該進程從兩個獨立的FD中讀取數據,只要其中一個FD可用。
  • 因此,我們需要兩個獨立的寫入器進程。
  • 保持兩個獨立作者的輸出不會互相破壞的唯一方法是確保它們都以附加模式打開它們的輸出。以追加模式打開的FD具有保證的屬性,即每次寫入數據時,都會首先跳到結尾。

所以:

# Bash 
> mylog 
exec > >(tee -a mylog) 2> >(tee -a mylog >&2) 

echo A >&2 
cat file 
echo B >&2 

這將確保日誌文件是正確的。它並不能保證作家的下一個shell提示符之前完成:

~$ ./foo 
A 
hi mom 
B 
~$ cat mylog 
A 
hi mom 
B 
~$ ./foo 
A 
hi mom 
~$ B 

另請參閱BashFAQ/002BashFAQ/047

+1

假設我們正在談論基於行的字符串,我們可以管兩個流通過,說,'sed'在行首添加標記?然後,我們可以從日誌文件中知道哪一行來自哪個流。 – Raphael 2017-05-10 09:04:45

+1

@Raphael:你應該能夠在每個進程替換內部的'tee'之前,例如:'exec>>(sed's/^/from STDOUT:/'| tee -a mylog)2>>(sed's/^ /來自STDERR:/'| tee -a mylog>&2)'(未經測試)。順便說一下,這將導致輸出到終端的也有這些前綴。 – 2017-05-10 14:24:28

+0

酷!這不會使您給出的部分報價無效嗎?現在我們有一個sychronized共享輸出文件,並且仍然知道哪個是哪個,或者我錯過了什麼? – Raphael 2017-05-10 18:20:28

2

這聽起來像是一個多任務的任務。

要測試同步輸出或磁盤寫入,而不使用進程替換可能要玩弄的Bash shell重定向技巧和T(1)類似以下內容:

echos() { echo "hello on stdout"; echo "hello on stderr" 1>&2; return 0; } 

    { 
    { 
    echos 3>&- | 
     tee stdout.log 2>&3 3>&- 
    } 2>&1 >&4 4>&- | 
     tee stderr.log 2>&3 3>&- 
} 3>&2 4>&1 2>&1 | tee /dev/stderr > all.log 

open -e stdout.log stderr.log all.log # Mac OS X 

欲瞭解更多信息,請參見:http://codesnippets.joyent.com/posts/show/8769

要獲取命令的退出碼,你可能要分別檢查出pipefail或PIPESTATUS(除了最後退出碼變量「$?」):

help set | grep -i pipefail 
man bash | less -p pipefail 
man bash | less -p PIPESTATUS 
+0

PIPESTATUS似乎不起作用 - 如果回聲命令返回1,它仍然只有兩個零。 – l0b0 2010-12-21 13:30:42

+0

'set -o pipefail'雖然工作。 – l0b0 2010-12-21 14:00:59

1

最後一次迭代,其中條的值設置爲標準錯誤的內容,請嘗試:

# restore original stdout & stderr at the end by adding: 1>&2 2>&1 
bar="$(
{ 
    { 
     echo foo >&2 | tee stdout.log 2>&3 3>&- 
    } 2>&1 >&4 4>&- | tee stderr.log 2>&3 3>&- 
} 3>&2 4>&1 1>&2 2>&1 
)" 

echo "$bar" 
相關問題