14

stderr を stdout と組み合わせる場合、 (パイプ)の前に (ファイルへのリダイレクト) の後2>&1に来る必要があるのはなぜですか?|> myfile

ファイル出力用に stderr を stdout にリダイレクトするには:

  echo > myfile 2>&1

パイプの stderr を stdout にリダイレクトするには:

  echo 2>&1 | less



私の仮定は、私ができることでした:

  echo | less 2>&1 

それは機能しますが、機能しません。なぜだめですか?

4

3 に答える 3

23

パイプラインは | で区切られたコマンドのリストです。指定したリダイレクトは構成コマンド (単純または複合) に適用されますが、パイプライン全体には適用されません。各パイプは、コマンドに関連付けられたリダイレクトが評価される前に、暗黙的に各サブシェルにリダイレクトを適用することにより、1 つのコマンドの stdout を次の stdin にチェーンします。

cmd 2>&1 | less

最初のサブシェルの最初の stdout は、less読み取り元のパイプにリダイレクトされます。次に、2>&1最初のコマンドにリダイレクトが適用されます。stdout は既にパイプを指しているため、stderr を stdout にリダイレクトすると機能します。

cmd | less 2>&1

ここで、リダイレクトは に適用されlessます。Less の stdout と stderr はどちらも端末を指していると思われるため2>&1、この場合は効果がありません。

リダイレクトをパイプライン全体に適用したり、パイプラインの一部として複数のコマンドをグループ化したり、パイプラインをネストしたりする場合は、コマンド グループ (またはその他の複合コマンド) を使用します。

{ { cmd1 >&3; cmd2; } 2>&1 | cmd3; } 3>&2

典型的な例かもしれません。最終結果は次のとおりです。 cmd1andcmd2の stderr -> cmd3; cmd2の標準出力 -> cmd3; およびの stderr、およびcmd1のstdout -> 端末。cmd3cmd3

Bash 固有の|&パイプを使用すると、パイプラインの stdout リダイレクトはそれぞれ最初に発生しますが、stderr リダイレクトは実際には最後に発生するため、事態はさらに奇妙になります。たとえば、次のようになります。

f() { echo out; echo err >&2; }; f >/dev/null |& cat

ここで、直感に反して、すべての出力が非表示になります。の最初の stdout がfパイプに移動し、次の stdout がfにリダイレクトされ/dev/null、最後に stderr が stdout (/dev/null静止) にリダイレクトされます。

|&Bashでは絶対に使用しないことをお勧めします。ここではデモ用に使用しています。

于 2012-07-20T00:05:05.643 に答える
8

ormaajの答えに追加するには:

リダイレクト演算子を適切な順序で指定する必要がある理由は、それらが左から右に評価されるためです。次のコマンド リストを検討してください。

# print "hello" on stdout and "world" on stderr
{ echo hello; echo world >&2; }

# Redirect stdout to the file "out"
# Then redirect stderr to the file "err"
{ echo hello; echo world >&2; } > out 2> err

# Redirect stdout to the file "out"
# Then redirect stderr to the (already redirected) stdout
# Result: all output is stored in "out"
{ echo hello; echo world >&2; } > out 2>&1

# Redirect stderr to the current stdout
# Then redirect stdout to the file "out"
# Result: "world" is displayed, and "hello" is stored in "out"
{ echo hello; echo world >&2; } 2>&1 > out
于 2012-07-20T00:33:55.887 に答える
3

私の答えは、ファイル記述子を理解することです。各プロセスには、一連のファイル記述子 (開かれているファイルへのエントリ) があります。デフォルトでは、番号 0 は stdin 用、番号 1 は stdout 用、番号 2 は stderr 用です。

i/o リダイレクタ > および < はデフォルトで、最も適切なファイル記述子である stout および stdin に接続します。stdout をファイルに再ルーティングする場合 (のようにfoo > bar)、プロセス 'foo' の開始時に、ファイル 'bar' が書き込み用に開かれ、ファイル記述子番号 1 にフックされます。 dfoo 2> barファイルバーを開き、それを stderr にフックする使用。

これで、I/O リダイレクタ '2>&1'. 私は通常、「ファイル記述子2をファイル記述子1と同じにする」と読みます。コマンドラインを左から右に読みながら、次のことができます:foo 1>bar 2>&1 1>/dev/tty. これにより、ファイル記述子 1 がファイル 'bar' に設定され、ファイル記述子 2 が 1 と同じ (したがって 'bar') に設定され、その後、ファイル記述子 1 が /dev/tty に設定されます。実行中は出力を /dev/ ttyfooに送信し、stderr をファイル 'bar' に送信しています。

ここでパイプラインが登場します。これはファイル記述子を変更しませんが、プロセス間でそれらを接続します。左のプロセスの stdout と次のプロセスの stdin です。Stderr が渡されます。したがって、パイプラインを stderr のみで動作させたい場合は、 を使用しますfoo 2| bar。これは、 の stderrfooを の stdin に接続しbarます。( の stdout で何が起こるかわかりませんfoo。)

上記の場合、 を使用するfoo 2>&1 | barと、 の stderr が のfoostdout に再ルーティングされるためfoo、 の stdout と stderr の両方fooが の stdin に到達しbarます。

于 2013-01-21T11:27:18.307 に答える