web-dev-qa-db-ja.com

stdoutをファイルにリダイレクトし、stdout + stderrを別のファイルにリダイレクトする方法は?

どうすれば達成できますか

cmd >> file1 2>&1 1>>file2

つまり、stdout and stderrは1つのファイル(file1)にリダイレクトし、stdout(file2)のみを別のファイルにリダイレクトする必要があります(両方とも追加モード)?

33
Swarna Gowri

問題は、出力をリダイレクトすると、次のリダイレクトで使用できなくなることです。サブシェルでteeにパイプして、2番目のリダイレクトの出力を保持できます。

( cmd | tee -a file2 ) >> file1 2>&1

またはターミナルで出力を見たい場合:

( cmd | tee -a file2 ) 2>&1 | tee -a file1

最初のteeのstderrをfile1に追加しないようにするには、コマンドのstderrをいくつかのファイル記述子(例:3)にリダイレクトし、後でこれを再度stdoutに追加する必要があります。

( 2>&3 cmd | tee -a file2 ) >> file1 3>&1
# or
( 2>&3 cmd | tee -a file2 ) 3>&1 | tee -a file1

(@ fra-sanに感謝)

42
pLumo

zshの場合:

cmd >& out+err.log > out.log

追加モードの場合:

cmd >>& out+err.log >> out.log

zshで提供され、mult_iosオプションは無効化されていません。ファイル記述子(ここでは1)が書き込みのために数回リダイレクトされると、シェルは組み込みteeを実装して、すべてのターゲットに出力を複製します。

16

Stdoutにタグを付けることができます(UNBUFFERED sedを使用します:sed -u ...)、stderrもstdout(タグ付けされたsedを通過しなかったため、タグ付けされていない)に移動し、結果のログファイルで2を区別できるようにします。

以下はis slow(たとえば、whileの代わりにPerlスクリプトを使用することにより、真剣に最適化できます...; do ...; done、たとえば、サブシェルとコマンドを生成しますすべての行で!)、weird(1つの名前変更stdoutに2つの{}ステージが必要であるように思われ、他の1つでは「フォールスルーされた」stderrをそれに追加します)など。 is:a "proof of concept"、これは出力の順序を可能な限りstdoutとstderrにできるだけ維持しようとします:

#basic principle (some un-necessary "{}" to visually help see the layers):
# { { complex command ;} | sed -e "s/^/TAGstdout/" ;} 2>&1 | read_stdin_and_redispatch

#exemple:
# complex command = a (slowed) ls of several things (some existing, others not)
#  to see if the order of stdout&stderr is kept

#preparation, not needed for the "proof of concept", but needed for our specific exemple setup:
\rm out.file out_AND_err.file unknown unknown2 
touch existing existing2 existing3

#and the (slow, too many execs, etc) "proof of concept":
uniquetag="_stdout_" # change this to something unique, that will NOT appear in all the commands outputs... 
                     # avoid regexp characters ("+" "?" "*" etc) to make it easy to remove with another sed later on.

{
   { for f in existing unknown existing2 unknown2 existing3 ; do ls -l "$f" ; sleep 1; done ;
   } | sed -u -e "s/^/${uniquetag}/" ;
} 2>&1 | while IFS="" read -r line ; do
    case "$line" in
       ${uniquetag}*) printf "%s\n" "$line" | tee -a out_AND_err.file | sed -e "s/^${uniquetag}//" >> out.file ;; 
        *)            printf "%s\n" "$line"       >> out_AND_err.file ;;   
    esac; 
done;

# see the results:
grep "^" out.file out_AND_err.file
3
Olivier Dulac

出力の順序が次のようでなければならない場合:stdout then stderr;リダイレクトのみのソリューションはありません。
stderrは一時ファイルに保存する必要があります

_cmd 2>>file-err | tee -a file1 >>file2
cat file-err >> file1
rm file-err
_

説明:

one出力(stdoutまたはstderrのようなfd)をtwoファイルにリダイレクトする唯一の方法は、それを再現することです。コマンドteeは、ファイル記述子の内容を再現するための正しいツールです。したがって、2つのファイルに1つの出力を持つという最初のアイデアは、次のようにすることです。

_... |  tee file1 file2
_

これにより、ティーのstdinが両方のファイル(1と2)に再現され、ティーの出力は未使用のままになります。しかし、追加する必要があります(_-a_を使用)。コピーは1つだけ必要です。これは両方の問題を解決します:

_... | tee -a file1 >>file2
_

teeにstdout(繰り返すもの)を提供するには、コマンドから直接stderrを使用する必要があります。 1つの方法として、順序が重要でない場合(出力の順序は(おそらく)生成されたまま保持されますが、最初に出力された方が最初に格納されます)。どちらか:

  1. _cmd 2>>file1 | tee -a file2 >>file1_
  2. cmd 2>>file1 > >( tee -a file2 >>file1 )
  3. _( cmd | tee -a file2 ) >> file1 2>&1_

オプション2は一部のシェルでのみ機能します。オプション3では、追加のサブシェル(低速)を使用しますが、ファイル名は1回だけ使用します。

ただし、stdoutmustが最初である場合(出力が生成される順序にかかわらず)、stderrを格納して、終了(最初のソリューションが投稿されました)。

2
Isaac

多様性のために:

システムが/dev/stderrをサポートしている場合、

(cmd | tee -a /dev/stderr) 2>> file1 >> file2

働くでしょう。 cmdの標準出力は、パイプラインのstdoutとstderrの両方に送信されます。 cmdの標準エラーはteeをバイパスし、パイプラインのstderrから出力されます。

そう

  • パイプラインのstdoutはcmdのstdoutであり、
  • パイプラインのstderrは、cmdのstdoutとstderrが混在しています。

そうすれば、これらのストリームを正しいファイルに送信するだけで済みます。

このようなほとんどすべてのアプローチと同様( Stéphane’s answer を含む)、file1は行が乱れる場合があります。

1
Scott