2010-12-21 43 views
9

テストの目的で、stdoutとstderr を個別にに保存して検査します。たとえば、誤った入力でテストを実行するとstderrに出力されますが、stdoutには出力されません。正しい入力でテストを実行するとstdoutに出力されますが、stderrには出力されません。テストで競合状態を避けるためには、保存は同期している必要があります(私はprocess substitutionを使用できません)。stdout、stderr、stdout + stderrを同期して保存します

事実の後にテストをデバッグできるようにするために、私はも出力された順番でstdoutとstderrを参照する必要があります。だから私はそれらを同じファイル/変数/に保存するか、別々に保存するのと同時にそれらを端末に送信する必要があります。

エラーが発生したことをテストするには、にコマンドの終了コードが必要です。

効率と正確さの理由から、私はもちろん各テストを2回実行することはできません。

例えばstdoutをstdout.logに、stderrをstderr.logに、の両方に同じコマンドでoutput.logにリダイレクトすることは可能ですか?または、同期teeコマンドをstdoutとstderrに別々に使用するには?または、stdoutとstderrのコピーを別々の変数に保存するには?

更新:ティムのソリューションは、ほとんどの作品のようにそれは(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間の分離の喪失、またはラインの非同期のいずれかを受け入れることはありません。彼らは純粋主義者なので、最も困難なものを求めています。私はstdoutとstderrをひとつのファイルにまとめて記録したいのですが、オリジナルの別々の目的地を維持したいと思っています。

これを行うために、我々は最初の数のノートを作成する必要があります。

  • する二つの別々のstdoutとstderrのストリームがあるように予定されている場合は、その後、いくつかのプロセスは、それらのそれぞれを記述する必要があります。
  • シェルにpoll(2)またはselect(2)インタフェースがないため、2つの別々のFDから読み取るプロセスをシェルスクリプトに書き込む方法はありません。
  • したがって、2つの個別のライタープロセスが必要です。
  • 2つの別々のライターからの出力を互いに破壊しないようにする唯一の方法は、出力が両方とも付加モードで出力されるようにすることです。追加モードで開かれたFDは、データが書き込まれるたびに最初に最後にジャンプすることが保証されたプロパティを持ちます。

ので:

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

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

これは、ログファイルが正しいことを保証します。

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

また、BashFAQ/002BashFAQ/047を参照してください。それは作家が次のシェルプロンプト前に完了することを保証するものではありません。

+1

我々はラインベースの文字列を話していると仮定すると、我々はパイプでは '、たとえば、sed'を通じストリームの両方が可能行の先頭にマーカーを追加するには?次に、ログファイルからどの行がどのストリームから来ているかを知ることができます。 – Raphael

+1

@Raphael:各プロセス置換の中で 'tee'の前に置くことができるはずです。' exec>>(sed 's/^/from STDOUT:/' | tee -a mylog)2>>(sed 's/^ /からSTDERR:/ '| tee -a mylog>&2) '(テストされていません)。これは、端末への出力にも、それらのプレフィックスをもたせることになります。 –

+0

クール!あなたが与える見積もりの​​一部を無効にしませんか?今私たちはsychronized共有出力ファイルを持っていますが、どちらが何か、または私は何かが不足しているかを知っている? – Raphael

2

これはマルチユーザの仕事のようです。詳細については、参照してください

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 

同期出力またはディスクをテストするには、次のようなバッシュシェルのリダイレクトのトリックやティー(1)で遊んでしたいことがあり、プロセス置換を使用せずに書き込み、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は動作していないようです - echosコマンドが1を返した場合、まだ2つのゼロだけです。 – l0b0

+0

'set -o pipefail'は動作します。 – l0b0

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" 
関連する問題