回答:
リダイレクションがなくても、または以外の>logfile 2>&1
場合でも、出力が生成順に表示されるとは限りません。
まず第一に、アプリケーションからの標準出力は行バッファリング(ttyに)またはバッファリング(パイプラインに)されますが、stderrはバッファリングされないので、読者に関する限り出力の順序の関係は壊れます。作成できるパイプラインの後続のステージは、2つのストリームへの決定論的に順序付けられたアクセスを取得しません(概念的には並行して発生するものであり、常にスケジューラーの対象になります-読者がすでにスライスを取得している場合、両方のパイプに書かれているため、どちらが最初に来たかはわかりません)。
「発生する順序」は、アプリケーションにのみ実際に知られています。stdout / stderrでの出力の順序付けは、よく知られている-古典的な、おそらく-問題です。
構築を使用する場合:stderrリダイレクトの前にstdoutリダイレクトが設定されるため、stderrとstdoutの1>stdout.log 2>&1
両方がファイルにリダイレクトされます。
あなたはあなたが得ることができる注文反転した場合は標準出力ファイルにリダイレクトし、その後、コピー標準エラー出力をする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!
tee -a
同じファイルで1>
両方の出力を合計するために、同じファイルで使用できます。次のようなものです./test 2>&1 1>out+err.log | tee -a out+err.log
wget -O - www.google.de
ません。(以下のソリューションと比較してください)
tee -a
何もリダイレクトされなかった場合、コンソールに表示される出力と同じ出力を使用するための信頼性の高い動作は行われません。経由tee
する出力は、コマンドから直接出力される出力と比べてわずかに遅れる場合があり、そのためログの後半に表示される場合があります。
bashスクリプトの先頭に次の行を配置することで、これを実現できました。
exec 1>>log 2> >(tee -a log >&2)
これは、stdoutをファイルにリダイレクトされますlog
(1>>log
ファイルへのティー標準エラー出力、その後、) (log
()2> >(tee -a log
とバックstderrにそれを指示>&2)
。このように、私は、1つのファイルを取得するlog
ために、stdoutとstderrの両方を示しており、および標準エラー出力でもあります通常どおり画面に表示されます。
キャッチは、ファイルに追加するときにのみ機能するようだということです。追加しなかった場合、2つのリダイレクトはお互いに重複しているように見え、どちらか一方の出力のみが最後に表示されます。
エラーストリームを複製して、コンソールとログファイルの両方に表示するようにします。そのためのツールはでありtee
、必要なのはエラーストリームにそれを適用することだけです。残念ながら、コマンドのエラーストリームを別のコマンドにパイプするための標準的なシェル構造はないため、少しファイル記述子の再配置が必要です。
{ { 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
tee
ここでも遅延が発生します。純粋なシェルソリューションがあるとは思わない。実際、何らかの方法でアプリケーションを変更せずに解決策があるかどうか疑問に思います。
stderrを画面に書き込み、stderrとstdoutの両方をファイルに書き込む-そして、stderrとstdoutの行を、両方が画面に書き込まれた場合と同じ順序で出力する:
困難な問題であることが判明しました。特に、単に画面にそれらを書き込んだ場合に予想される「同じシーケンス」についての部分です。簡単に言うと、それぞれを独自のファイルに書き込み、バックグラウンド処理を行って、各行(各ファイル)に行が生成された正確な時間をマークし、その後、「tail --follow」stderrファイルを画面に表示します。ただし、「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
元の質問が尋ねられてからずっと後にここに到着した誰かを助けてくれることを願っています。
Letを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 )
私はこの正確な要件に苦労しましたが、最終的には簡単な解決策を見つけることができませんでした。私が代わりにやったことはこれでした:
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
stderrからcommand-置換tee -a
、およびstdoutを同じファイルに追加する:
./script.sh 2> >(tee -a outputfile) >>outputfile
注:正しい順序を確認してください(ただし、stderr-showなし)。「expect」ツールから「unbuffer」コマンドがあります。これは、端末で表示されるように、ttyをシミュレートし、stdout / errを順番に保持します。
unbuffer ./script.sh > outputfile