web-dev-qa-db-ja.com

stdoutとstderrをシェルスクリプトの2つの異なるプロセスにパイプしますか?

私はただやっているパイプラインを持っています

 command1 | command2

したがって、command1のstdoutはcommand2に移動し、command1のstderrはターミナル(またはシェルのstdoutがどこにあるか)に移動します。

Command1のstderrを3番目のプロセス(command3)stdoutがまだcommand2に行く間?

55
user964970

別のファイル記述子を使用する

{ command1 2>&3 | command2; } 3>&1 1>&2 | command3

最大7つの他のファイル記述子を使用できます(3から9まで)。
さらに説明が必要な場合は、お問い合わせください。説明できます;-)

テスト

{ { echo a; echo >&2 b; } 2>&3 | sed >&2 's/$/1/'; } 3>&1 1>&2 | sed 's/$/2/'

出力:

b2
a1

2つのログファイルを生成します。
1。 stderrのみ
2。 stderrおよびstdout

{ { { command 2>&1 1>&3; } | tee err-only.log; } 3>&1; } > err-and-stdout.log

commandecho "stdout"; echo "stderr" >&2その後、次のようにテストできます。

$ { { { echo out>&3;echo err>&1;}| tee err-only.log;} 3>&1;} > err-and-stdout.log
$ head err-only.log err-and-stdout.log
==> err-only.log <==
err

==> err-and-stdout.log <==
out
err
53
olibre

受け入れられた回答により、stdoutstderrが逆になります。それらを保持する方法は次のとおりです(その目的でグーグルするとこの投稿が表示されるため)。

{ command 2>&1 1>&3 3>&- | stderr_command; } 3>&1 1>&2 | stdout_command

通知:

  • fdcommandに継承されないようにするには、3>&-が必要です。 (これは、commandが内部で何をするかによって、予期しない結果をもたらす可能性があるためです。)

パーツの説明:

  1. 最初に外側の部分:

    1. 3>&1-fd for { ... }fd 1に設定されます(つまり、stdout
    2. 1>&2-fd 1 for { ... }fd 2に設定されます(つまり、stderr
    3. | stdout_command-fd 1(以前はstdout)はstdout_commandを介してパイプされます
  2. 内部は外部からファイル記述子を継承します:

    1. 2>&1-fd 2 for commandfd 1に設定されます(つまり、外部パーツごとにstderr
    2. 1>&3-fd 1 for commandfdが何であったかに設定されます(つまり、stdoutは外部パーツごとに)
    3. 3>&--fd for commandは何も設定されていません(つまりclosed
    4. | stderr_command-fd 1(以前はstderr)はstderr_commandを介してパイプされます

例:

foo() {
    echo a
    echo b >&2
    echo c
    echo d >&2
}

{ foo 2>&1 1>&3 3>&- | sed -u 's/^/err: /'; } 3>&1 1>&2 | sed -u 's/^/out: /'

出力:

out: a
err: b
err: d
out: c

a -> cb -> dの間には同期の形式がないため、stderr_commandstdout_commandの順序は常に不確定です。)

25
antak

Stderrをstdoutにリダイレクトするだけです

{ command1 | command2; } 2>&1 | command3

注意:commnd3は、command2 stdout(存在する場合)も読み取ります。
これを回避するには、commnd2 stdoutを破棄します。

{ command1 | command2 >/dev/null; } 2>&1 | command3

ただし、command2 stdoutを保持するため(たとえば、ターミナル内)、
次に、他の私の回答をより複雑に参照してください。

テスト

{ { echo -e "a\nb\nc" >&2; echo "----"; } | sed 's/$/1/'; } 2>&1 | sed 's/$/2/'

出力:

a2
b2
c2
----12
13
olibre

プロセス置換の使用:

command1 > >(command2) 2> >(command3)

詳細は http://tldp.org/LDP/abs/html/process-sub.html を参照してください。

5
FuePi

同じ効果はfifoでかなり簡単に達成できます。私はそれを行うための直接パイプ構文を知りません(それを見るのは気の利いたことです)。これは、FIFOでそれを行う方法です。

まず、stdoutstderrouterr.shの両方に出力されるもの:

#!/bin/bash

echo "This goes to stdout"
echo "This goes to stderr" >&2

その後、次のようなことができます。

$ mkfifo err
$ wc -c err &
[1] 2546
$ ./outerr.sh 2>err | wc -c
20
20 err
[1]+  Done                    wc -c err

この方法では、最初にstderr出力のリスナーを設定し、構文2>errを使用して、次のコマンドで発生するライターが作成されるまでリスナーをブロックします。各wc -cに20文字の入力があることがわかります。

使い続けたくない場合は、完了後にfifoをクリーンアップすることを忘れないでください(つまり、rm)。他のコマンドがファイル引数ではなくstdinへの入力を必要とする場合、wc -c < errのような入力リダイレクトを使用することもできます。

1
FatalError

通常どおりstdoutをパイプしますが、stderrリダイレクトにはBashプロセスの置換を使用します。

some_command 2> >(command of stderr) | command of stdout

ヘッダー:#!/bin/bash

0
iBug