パイプで「T」を使用しているときにstderrをファイルに書き込む方法を教えてください。


543

端末に表示しながら、toのtee出力(STDOUT)を書き込む方法を知っています。aaa.shbbb.out

./aaa.sh | tee bbb.out

表示したまま、STDERRという名前のファイルに書き込むにはどうすればよいccc.outですか?


2
明確にするために、stderrをファイルだけでなく画面にも移動させますか?
Charles Duffy

私はそうしました、それを明確にするために私の投稿を編集します。lhunathのソリューションで十分だと思います。助けてくれてありがとう!
jparanich 2009年

回答:


785

端末でSTDERRとSTDOUTを引き続き表示したいと思います。あなたはジョシュ・ケリーの答えに行くことができますが、私はtailあなたのログファイルを出力するバックグラウンドで周りを非常にハッキーで無愛想に保つと思います。exra FDを維持し、後でkillしてクリーンアップを行う必要があることに注意してくださいtrap '...' EXIT。技術的にはで行う必要があります。

これを行うためのより良い方法があり、すでにそれを発見しました:tee

ただ、それをstdoutに使用する代わりに、stdoutとtderrにT型を使用します。これをどのように達成しますか?プロセスの置換とファイルのリダイレクト:

command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)

それを分割して説明しましょう:

> >(..)

>(...)(プロセス置換)FIFOを作成し、それを待機させますtee。次に、>(ファイルリダイレクト)を使用しcommandて、最初のSTDOUTをFIFO にリダイレクトします。teeがリッスンし。

2番目も同じです。

2> >(tee -a stderr.log >&2)

プロセス置換を再度使用teeして、STDINから読み取り、それをにダンプするプロセスを作成しますstderr.logtee入力をSTDOUTに出力しますが、その入力はSTDERRなので、teeのSTDOUTを再びSTDERR にリダイレクトします。次に、ファイルリダイレクトを使用してcommandのSTDERRをFIFOの入力(teeのSTDIN)にます。

見る http://mywiki.wooledge.org/BashGuide/InputAndOutputをください

プロセスの置換はbash、シェルsh(POSIXやBourne)とは対照的に、シェルとして選択することのボーナスとして得られる本当に素敵なものの1つです。


ではsh、手動で行う必要があります。

out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log < "$out" &
tee -a stderr.log < "$err" >&2 &
command >"$out" 2>"$err"

5
私はこれを試しました:$ echo "HANG" > >(tee stdout.log) 2> >(tee stderr.log >&2)動作しますが、入力を待ちます。これが起こる単純な理由はありますか?
ジャスティン

1
@SillyFreak何をしたいのか、何が問題なのかわかりません。エコーテスト; exitはstdoutに出力を生成しないため、errは空のままになります。
lhunath 2013

1
そのコメントをありがとう; その後、論理エラーが何であるかを理解しました。対話型シェルとして呼び出されると、bashはコマンドプロンプトを出力し、exderをstderrにエコーします。ただし、stderrがリダイレクトされる場合、bashはデフォルトで非対話型として起動します。比較/bin/bash 2> errして/bin/bash -i 2> err
Silly Freak

15
そして、「見ることは信じている」という人のために、簡単なテスト:(echo "Test Out";>&2 echo "Test Err") > >(tee stdout.log) 2> >(tee stderr.log >&2)
マシュー・ウィルコクソン'27年

2
すごい。良い答え:「分割して説明しましょう」+1
jalanb

673

なぜ単純ではないのですか?

./aaa.sh 2>&1 | tee -a log

これは単ににリダイレクトされるstderrためstdout、Teeはログと画面の両方にエコーします。他の解決策のいくつかは本当に複雑に見えるので、私は何かが足りないかもしれません。

注: bashバージョン4以降|&、以下の略語として使用できます2>&1 |

./aaa.sh |& tee -a log

85
stdout(チャネル1)とstderr(チャネル2)の両方を同じファイル(stdoutとsterrの両方が混在する単一のファイル)に記録する場合は、これで問題ありません。もう1つのより複雑なソリューションでは、stdoutとstderrを2つの異なるファイル(それぞれstdout.logとstderr.log)に分離できます。それが重要な場合もあれば、そうでない場合もあります。
タイラーリック

19
他のソリューションは、多くの場合、必要以上に複雑です。これは私には完璧に動作します。
dkamins

13
この方法の問題は、aaa.shプロセスから終了/ステータスコードを失うことです。これは、重要な場合があります(たとえば、makefileで使用する場合)。あなたは受け入れられた答えでこの問題を抱えていません。
Stefaan 2013年

9
マージされたstdout / stderrが気にならない場合は、./aaa.sh |& tee aaa.log(bashで)機能します。
jfs 2013

5
私はあなたがコマンドチェインを付加した場合、あなたは終了ステータスを維持することができると信じて@Stefaan set -o pipefailが続く;&&どうかは間違っていません。
デビッド

59

これはグーグルを介してこれを見つける人々のために役立つかもしれません。試してみたい例のコメントを外してください。もちろん、出力ファイルの名前は自由に変更してください。

#!/bin/bash

STATUSFILE=x.out
LOGFILE=x.log

### All output to screen
### Do nothing, this is the default


### All Output to one file, nothing to the screen
#exec > ${LOGFILE} 2>&1


### All output to one file and all output to the screen
#exec > >(tee ${LOGFILE}) 2>&1


### All output to one file, STDOUT to the screen
#exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)


### All output to one file, STDERR to the screen
### Note you need both of these lines for this to work
#exec 3>&1
#exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)


### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen
#exec > ${STATUSFILE} 2>${LOGFILE}


### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen
#exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)


### STDOUT to STATUSFILE and screen, STDERR to LOGFILE
#exec > >(tee ${STATUSFILE}) 2>${LOGFILE}


### STDOUT to STATUSFILE, STDERR to LOGFILE and screen
#exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)


echo "This is a test"
ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaff
ls -l ${0}

6
いいえ、私はexecが説明を使用できると思います。exec >つまり、ファイル記述子のターゲットを特定の宛先に移動します。デフォルトは1なので、exec > /dev/nullこのセッションでは、stdoutの出力を/ dev / null に移動します。このセッションの現在のファイル記述子は、を実行して確認できますls -l /dev/fd/。それを試してみてください!次にexec 2>/tmp/stderr.log.、発行時に何が起こるかを確認します。さらに、exec 3>&1番号3の新しいファイル記述子を作成し、それをファイル記述子1のターゲットにリダイレクトします。例では、ターゲットはコマンドが発行されたときの画面でした。
2016年

画面と個別のファイルの両方にstdoutとstderrを表示する例は素晴らしいです!!! どうもありがとう!
Marcello de Sales

21

stderrをファイルにリダイレクトするには、stdoutを画面に表示し、stdoutをファイルに保存します。

./aaa.sh 2> ccc.out | ティー./bbb.out

編集:stderrとstdoutの両方を画面に表示し、両方をファイルに保存するには、bashのI / Oリダイレクトを使用できます。

#!/bin/bash

# Create a new file descriptor 4, pointed at the file
# which will receive stderr.
exec 4<>ccc.out

# Also print the contents of this file to screen.
tail -f ccc.out &

# Run the command; tee stdout as normal, and send stderr
# to our file descriptor 4.
./aaa.sh 2>&4 | tee bbb.out

# Clean up: Close file descriptor 4 and kill tail -f.
exec 4>&-
kill %1

1
明示的には指定されていませんが、ユーザーはファイルに加えてstderrをコンソールに移動させたいと思っています。
Charles Duffy

2
私はもっ​​とはっきりしているべきだった、私も画面にstderrを望んでいた。私はジョシュケリーの解決策を今でも楽しんでいましたが、lhunathの方が自分のニーズにより合うものを見つけました。みんなありがとう!
jparanich 2009年

18

つまり、1つのフィルター(tee bbb.out)にstdoutをパイプし、別のフィルター(tee ccc.out)にstderr をパイプする必要があります。stdout以外を他のコマンドにパイプする標準的な方法はありませんが、ファイル記述子をジャグリングすることで回避できます。

{ { ./aaa.sh | tee bbb.out; } 2>&1 1>&3 | tee ccc.out; } 3>&1 1>&2

標準エラーストリーム(stderr)をgrepする方法も参照してくださいそしていつ追加のファイル記述子を使用しますか?

bash(およびkshとzsh)では、dashなどの他のPOSIXシェルでは、プロセス置換を使用できます。

./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out)

bashでは./aaa.shteeコマンドがまだ実行されている場合でも、このコマンドは終了するとすぐに戻ることに注意してください(kshとzshはサブプロセスを待機します)。これは、のような場合に問題になる可能性があります./aaa.sh > >(tee bbb.out) 2> >(tee ccc.out); process_logs bbb.out ccc.out。その場合は、代わりにファイル記述子ジャグリングまたはksh / zshを使用してください。


4
これは、stdout / stderrストリームをそのまま維持することを許可する唯一の回答のようです(たとえば、それらをマージしません)。甘い!
user1338062 2015年

shプロセスの置換が利用できないcronジョブに役立つ、の最も単純なアプローチ。
Roger Dueck

13

bashを使用している場合:

# Redirect standard out and standard error separately
% cmd >stdout-redirect 2>stderr-redirect

# Redirect standard error and out together
% cmd >stdout-redirect 2>&1

# Merge standard error with standard out and pipe
% cmd 2>&1 |cmd2

クレジット(頭から答えない)はここにあります:http : //www.cygwin.com/ml/cygwin/2003-06/msg00772.html


5

私の場合、スクリプトはstdoutとstderrの両方をファイルにリダイレクトするときにコマンドを実行していました。

cmd > log 2>&1

障害が発生したときに、エラーメッセージに基づいていくつかのアクションを実行するように更新する必要がありました。もちろん、dup 2>&1を削除してスクリプトからstderrをキャプチャすることもできますが、エラーメッセージは参照用にログファイルに記録されません。@lhunathからの受け入れられた回答は同じことをするはずですが、リダイレクトstdoutstderrて別のファイルにリダイレクトします。

(cmd 2> >(tee /dev/stderr)) > log

上記では、ログは、両方のコピーを持つことになりますstdoutstderr、私はキャプチャすることができますstderr気にすることなく、私のスクリプトからstdout


4

以下は、プロセス置換が利用できないKornShell(ksh)で機能します。

# create a combined(stdin and stdout) collector
exec 3 <> combined.log

# stream stderr instead of stdout to tee, while draining all stdout to the collector
./aaa.sh 2>&1 1>&3 | tee -a stderr.log 1>&3

# cleanup collector
exec 3>&-

ここでの本当のトリックは、2>&1 1>&3私たちのケースではstderrstdoutにリダイレクトし、stdoutを記述子にリダイレクトするシーケンスです3。この時点stderrstdoutはまだ結合されていません。

実際、stderr(としてstdin)はtee、ログの記録先に渡されますstderr.log記述子3にもリダイレクトされます。

そして記述子3combined.log常にそれを記録しています。だから、combined.log両方が含まstdoutstderr


気の利いた!それは素晴らしいオプションなので、過小評価されている同情。私はKSHを持っています、ところで:)
runlevel0

2

あなたが使用している場合のzshをあなたも必要ありませんので、あなたは、複数のリダイレクトを使用することができますtee

./cmd 1>&1 2>&2 1>out_file 2>err_file

ここでは、単純に各ストリームをそれ自体ターゲットファイルにリダイレクトしています。


完全な例

% (echo "out"; echo "err">/dev/stderr) 1>&1 2>&2 1>/tmp/out_file 2>/tmp/err_file
out
err
% cat /tmp/out_file
out
% cat /tmp/err_file
err

これにはMULTIOSオプションを設定する必要があることに注意してください(これがデフォルトです)。

MULTIOS

複数のリダイレクトが試行されたときに暗黙teeのsまたはcatsを実行しますリダイレクトを参照)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.