画面にstderrのみを表示し、stdoutとstderrをファイルに書き込みます。

画面にstderrのみを表示し、stdoutとstderrをファイルに書き込みます。

これを達成するためにBASHマジックをどのように使用できますか?
私は画面でstderr出力を見たいのですが、
stdoutとstderrの両方がファイルに書き込まれることを望みます。

説明:stdoutとstderrが同じファイルに入るようにしたいと思います。発生順に。
残念ながら、以下の答えのどれもこれに該当しません。

答え1

リダイレクトがない場合や他に何もない場合でも、生成さ>logfile 2>&1れた順序で出力が表示されるという保証はありません。

まず、アプリケーションのstdoutはラインバッファリング(ttyに)またはバッファリング(パイプに)されますが、stderrはバッファリングされていないため、読者が関係する限り、出力順序間の関係は壊れます。構築可能なパイプラインの後続の段階では、両方のストリームに対して決定論的に順番にアクセスすることはできません(概念的には並列に発生し、常にスケジューラによって制限されます。)読者がスライスを取得するまで、両方のパイプに記録し、どちらが最初に来たのかわかりません)。

「発生順序」は、アプリケーションでのみ実際に知ることができます。 stdout / stderr間の出力順序はよく知られている(おそらく古典的な)問題です。

答え2

構造を使用する場合:1>stdout.log 2>&1両方標準エラーそして標準出力このファイルにリダイレクトされるため標準出力リダイレクトが以前に設定されました。標準エラーリダイレクト。

順序を逆にすれば得ることができる。標準出力ファイルにリダイレクトするそれからコピー標準エラー到着標準出力これによりパイプで接続できますtee

$ cat test
#!/bin/sh
echo OUT! >&1
echo ERR! >&2

$ ./test 2>&1 1>stdout.log | tee stderr.log
ERR!

$ cat stdout.log
OUT!

$ cat stderr.log
ERR!

答え3

私のbashスクリプトの上部に次の行を追加することでこれを達成できました。

exec 1>>log 2> >(tee -a log >&2)

これはstdoutをfile log1>>log)にリダイレクトし、次にstderrをfile log2> >(tee -a log)にリダイレクトしてからstderr()にリダイレクトします>&2)。これにより、stdoutとstderrを順番に表示するファイルが得られ、logstderrも画面に通常どおり表示されます。

問題は、ファイルに追加したときにのみ機能するようです。接続しないと、両方のリダイレクトが互いに破壊されるように見え、最後の出力のみが得られます。

答え4

私はこの正確な要件のために困難を経験し、最終的に簡単な解決策を見つけることができませんでした。私がしたことは次のとおりです。

TMPFILE=/tmp/$$.log
myCommand > $TMPFILE 2>&1
[ $? != 0 ] && cat $TMPFILE
date >> myCommand.log
cat $TMPFILE >> myCommand.log
rm -f $TMPFILE

正しいインターリーブ順序でログファイルに stdout と stderr を追加します。エラーが発生した場合(コマンドの終了状態によって決まります)、出力全体(stdout)を送信します。そしてstderr)をstdoutに再び正しいインターリーブ順に変換します。私はこれが私のニーズに合理的な妥協だと思いました。増加する複数の実行ログファイルの代わりに単一の実行ログファイルが必要な場合は、次の方が簡単です。

myCommand > myCommand.log 2>&1
[ $? != 0 ] && cat myCommand.log

関連情報