これを達成するためにBASHマジックをどのように使用できますか?
画面にstderr出力のみを表示したい
しかし、stdoutとstderrの両方をファイルに書き込む必要があります。
明確化:stdoutとstderrの両方を同じファイルに入れたい。それらが起こる順序で。
残念ながら、以下の回答のいずれもこれを行いません。
リダイレクトせず、または>logfile 2>&1
、生成順に出力が表示されるとは限りません。
手始めに、アプリケーションからのstdoutはラインバッファリング(ttyへ)またはバッファリング(パイプラインへ)されますが、stderrはバッファリングされないため、リーダーに関する限り、出力の順序間の関係は壊れます。パイプラインの後続ステージでは、2つのストリームへのアクセスが確定的に順序付けられません(これらは概念的に並行して発生するものであり、常にスケジューラの影響を受けます-リーダーがスライスを取得するまでに、ライターがすでにスライスを取得している場合両方のパイプに書き込まれた場合、どちらが最初に来たかはわかりません)。
「彼はそれらが起こることを命じます」は本当にアプリケーションに知られています。 stdout/stderrでの出力の順序付けは、よく知られている-古典的な、おそらく-問題です。
構造を使用する場合:1>stdout.log 2>&1
stderrとstdoutの両方がファイルにリダイレクトされますstdoutリダイレクトは、stderrリダイレクトの前に設定されます。
順序を逆にすると、stdoutをファイルにリダイレクトして、thenコピーstderrを取得できますからstdoutにパイプして、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!
Bashスクリプトの先頭に次の行を配置することで、これを実現できました。
exec 1>>log 2> >(tee -a log >&2)
Stdoutをファイルlog
(1>>log
)、次にstderrをファイルlog
(2> >(tee -a log
)をstderr(>&2)
。このようにして、stdoutとstderrの両方を順番に表示する単一のファイルlog
を取得し、stderrも通常どおり画面に表示されます。
問題は、ファイルに追加したときにのみ機能するように見えることです。追加しないと、2つのリダイレクトが互いに干渉しているように見え、最後に出力するのは1つだけです。
私はこの正確な要件に苦労しており、結局は簡単な解決策を見つけることができませんでした。代わりに私がやったことはこれでした:
TMPFILE=/tmp/$$.log
myCommand > $TMPFILE 2>&1
[ $? != 0 ] && cat $TMPFILE
date >> myCommand.log
cat $TMPFILE >> myCommand.log
rm -f $TMPFILE
Stdoutとstderrを適切なインターリーブ順でログファイルに追加します。そして、エラーが発生した場合(コマンドの終了ステータスによって判断)、出力全体(stdout and stderr)をstdoutに送信します。これも適切なインターリーブ順です。これは私のニーズに対して妥当な妥協案であることがわかりました。増加する複数実行ログファイルではなく、1実行ログファイルが必要な場合は、さらに簡単です。
myCommand > myCommand.log 2>&1
[ $? != 0 ] && cat myCommand.log
Stderrを画面に書き込み、stderrとstdoutの両方をファイルに書き込むには、stderrとstdoutの両方の行が画面に書き込まれた場合と同じ順序で出力されるようにします。
難しい問題であることが判明しました。特に、単に画面に書き込んだ場合に予想される「同じシーケンス」を持つ部分です。簡単に言うと、それぞれを独自のファイルに書き込み、バックグラウンドプロセスのマジックを実行して、(各ファイルの)各行に、その行が生成された正確な時刻をマークします。次に、次のようにします。ただし、「stderr」と「stdout」の両方を順番に表示するには、2つのファイルを(各行に正確な時刻のマークを付けて)一緒に並べ替えます。
コード:
# Set the location of output and the "first name" of the log file(s)
pth=$HOME
ffn=my_log_filename_with_no_extension
date >>$pth/$ffn.out
date >>$pth/$ffn.err
# Start background processes to handle 2 files, by rewriting each one line-by-line as each line is added, putting a label at front of line
tail -f $pth/$ffn.out | Perl -nle 'use Time::HiRes qw(time);print substr(time."0000",0,16)."|1|".$_' >>$pth/$ffn.out.txt &
tail -f $pth/$ffn.err | Perl -nle 'use Time::HiRes qw(time);print substr(time."0000",0,16)."|2|".$_' >>$pth/$ffn.err.txt &
sleep 1
# Remember the process id of each of 2 background processes
export idout=`ps -ef | grep "tail -f $pth/$ffn.out" | grep -v 'grep' | Perl -pe 's/\s+/\t/g' | cut -f2`
export iderr=`ps -ef | grep "tail -f $pth/$ffn.err" | grep -v 'grep' | Perl -pe 's/\s+/\t/g' | cut -f2`
# Run the command, sending stdout to one file, and stderr to a 2nd file
bash mycommand.sh 1>>$pth/$ffn.out 2>>$pth/$ffn.err
# Remember the exit code of the command
myexit=$?
# Kill the two background processes
ps -ef | Perl -lne 'print if m/^\S+\s+$ENV{"idout"}/'
echo kill $idout
kill $idout
ps -ef | Perl -lne 'print if m/^\S+\s+$ENV{"iderr"}/'
echo kill $iderr
kill $iderr
date
echo "Exit code: $myexit for '$listname', list item# '$ix', bookcode '$bookcode'"
はい、これは手の込んだようで、4つの出力ファイル(そのうち2つは削除できます)になります。これは解決するのが難しい問題であると思われるため、いくつかのメカニズムが必要でした。
最後に、期待どおりの順序でstdoutとstderrの両方の結果を確認するには、次のコマンドを実行します。
cat $pth/$ffn.out.txt $pth/$ffn.err.txt | sort
シーケンスが、stdoutとstderrの両方が単に画面に表示された場合に少なくとも非常に近い理由は、すべての行にミリ秒までのタイムスタンプが付いていることです。
プロセスの進行中に画面にstderrを表示するには、次のコードを使用します。
tail -f $pth/$ffn.out
元の質問が尋ねられてからずっと後にここに到着した人を助けてくれることを願っています。
エラーストリームを複製して、コンソールとログファイルの両方に表示されるようにします。そのためのツールはtee
であり、エラーストリームに適用するだけで済みます。残念ながら、コマンドのエラーストリームを別のコマンドにパイプする標準のShell構造はないため、少しファイル記述子の再配置が必要です。
{ { echo out; echo err 1>&2; } 2>&1 >&3 | tee /dev/tty; } >log 3>&1
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^
command produces output stdout→3 →log
command produces error stderr→1 dup to terminal →log
私がここで読んだことを考えると、私が見ることができる唯一の解決策は、STOUTかSTERRかをタイムスライス/タイムスタンプで各行に付加することです。日付+%sは秒単位になりますが、順序を維持するのに時間がかかります。また、ログはどういうわけかソートする必要があります。私ができる以上の仕事。
stderrからコマンド置換tee -a
、および同じファイルに追加するstdout:
./script.sh 2> >(tee -a outputfile) >>outputfile
注:正しい順序を確認してください(ただし、stderr-showを使用しない)。「expect」ツールからの「unbuffer」コマンドがあり、ttyをシミュレートし、端末に表示される順序でstdout/errを保持します。
unbuffer ./script.sh > outputfile
f
を実行したいコマンドとすると、これは
( exec 3>/tmp/log; f 2>&1 1>&3 |tee >(cat)>&3 )
あなたが望むものをあなたに与えるはずです。例えば wget -O - www.google.de
は次のようになります。
( exec 3>/tmp/googlelog; wget -O - www.google.de 2>&1 1>&3 |tee >(cat)>&3 )