`...>>(sed's/^/stdout: /')` は印刷されません。 sed 's / ^ / stdout:/ 'が空の標準入力に印刷されるのはなぜですか?

`...>>(sed's/^/stdout: /')` は印刷されません。 sed 's / ^ / stdout:/ 'が空の標準入力に印刷されるのはなぜですか?

私は機能的に同じと思われる2つの構造の違いを引き起こす原因を理解しようとしています。

$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') | sed 's/^/stdout: /'   
stdout: stderr: foo
$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') > >(sed 's/^/stdout: /')
stderr: foo

編集:user1133275を正しく理解した場合、彼は標準出力に出力しないとサブシェルが>(sed 's/^/stdout: /')実行されない( echo foo >&2 )ことを提案しました。しかし、これは次のことを意味します。

$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') > >(echo baz)   
baz
stderr: foo

表示しないでくださいbaz

stdout:編集2:おそらく興味深いことに、sedはパイピング中に空の入力に対して出力しません。

$ sed 's/^/stdout: /' < /dev/null
$ printf "" | sed 's/^/stdout: /'
$

答え1

最初のコマンド、

( echo foo >&2 ) 2> >(sed 's/^/stderr: /') | sed 's/^/stdout: /'

単純化された形式(生成されたデータを保存するために一時ファイルを使用echo):

{ echo foo 2>file >&2; sed 's/^/stderr: /' file; } | sed 's/^/stdout: /'

つまり、最初のものはsed標準エラーの結果の内容を読み取り、echoそれを標準出力に書き込み、2番目のものはsedそれを読み取って修正します。

2番目のコマンドは

( echo foo >&2 ) 2> >(sed 's/^/stderr: /') > >(sed 's/^/stdout: /')

単純化された形で、

echo foo 2>file >&2; sed 's/^/stderr: /' file; sed 's/^/stdout: /' </dev/null

ここでsed、stderrを取得することは出力を生成しますが、stderr(何もなし)を取得する他はsed出力を生成しません(入力が得られず、データが挿入または追加されないため)。

他の表現として:

最初のコマンド:

( echo foo >&2 ) 2>file
sed 's/^/stderr: /' file | sed 's/^/stdout: /'

2番目のコマンド:

( echo foo >&2 ) 2>file >otherfile
sed 's/^/stderr: /' file
sed 's/^/stdout: /' otherfile

つまり、sed2つのコマンドのうち2番目のコマンドは何も読みません。特に、最初のコマンドと同様に、最初のコマンドの出力を読み取ることはありませんsed


非常に単純化された表記法を使用すると、最初のコマンドは次のようになります。

utility-writing-to-stderr 2> >(something1) | something2

これはsomething1標準出力に書き込まれますsomething2

2番目のコマンドは同じ表記法を使用します。

utility-writing-to-stderr 2> >(something1) >(something2)

つまり、互いに連結されていsomething1なくても何らかの方法で生成される内容を読み取ることができません。さらに、標準出力ストリームでは何も生成されないため、標準入力から何も読み取れません。something2something2something1utility-writing-to-stderrsomething2

答え2

>シェルのリダイレクト順序のために動作し、( echo foo >&2 )動作 |します。>(sed 's/^/stderr: /')例えば

$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /' > >(sed 's/^/stdout: /') )
stdout: stderr: foo

または

$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') | cat > >(sed 's/^/stdout: /')
stdout: stderr: foo

明確な例として、質問を順番に書いてください。

$ ( ( echo foo >&2 ) > >(sed 's/^/stdout: /') ) 2> >(sed 's/^/stderr: /')
stderr: foo

または

$ ( ( echo foo >&2 )  | sed 's/^/stdout: /' ) 2> >(sed 's/^/stderr: /')
stderr: foo

答え3

これら2つのコマンドは同じではありません。

コマンド(1):

( one ) 2> >(two) | three 

(std)出力をtwo次に送信しますthree(stderrの詳細については、以下を参照)。

コマンド(2):

( one ) 2> >(two) > >(three)

(std)出力を(stderr)oneに送信します。threetwo


胸の痛みの詳細は次のとおりです。

コマンド1:( one ) 2> >(two) | three

  • コマンドthreestart( sed 's/^/stdout: /') は標準入力の入力を待ちます。
  • 2stderrstdinは〜にリダイレクトされますtwo
  • one内部リダイレクト(echo foo >&2)が発生しました。
  • コマンドが実行されますfoostderr標準出力には送信されません)。
  • 単語はfoo(of)から(of)にリダイレクトされます。stderronestdintwo
  • 両方のコマンドを実行します(sed 's/^/stderr: /'
  • twoModified()出力がstderr: foostdoutに送信されますtwo
  • 現在の標準出力はtwoパイプを介して到着しますthree
  • three入力コマンドが最初から出力を受け取るのを待ちます。
  • 出力が修正され、リーダーが追加されますstdout:
  • 最後の文字列はstdout: stderr: foottyに移動します。

コマンド 2: (1) 2 >> (2) >> (3)

  • 最後のリダイレクト(>)が最初に構築されます。
  • コマンドがthree入力待ちを開始します。stdin ただ>())。
  • このコマンドthreeただ標準出力one
  • コマンドがtwo入力待ちを開始します。
  • このコマンドはtwoただ)標準エラーone
  • 内部リダイレクトはstdoutをoneここにリンクしますstderr
  • コマンドは(of)としてone送信され、実行されます。foostderrone
  • foo行くtwo
  • two受信した文字列を変更してstderr: foo次に送信します。端末
  • threestdoutから空の入力を受け取りますone
  • three印刷何もない(処理する行がないからです。)

このコマンドについて学びます。

printf '' | sed 's/^/initial: /'

出力がありません(そして動作させる方法もありません)。


変更の順序: コマンド 3: (1) >>(3) 2>>(2)

two練習としてthree詳細出力の代わりにに行きなさい。tty

答え4

私は@Kusalanandaの非常に明確な答えに同意しますが、仕事でのプロセス置換の小さな例として、彼の答えにこれを追加する危険があります。

2番目の提案は、小さな印刷上の変更(および誤ったロジックの比較的大きな変更)として実装できます。

  $ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') > >(sed 's/^/stdout: /')
  stderr: foo    # for the well explained reason above

しかし:

    v                                          vv
  $ ( (echo foo >&2) 2> >(sed 's/^/stderr: /') )> >(sed 's/^/stdout: /')  
  stdout: stderr: foo

( )>示されているように追加すると(または両方がうまく機能します>( ))、実際にはプロセス置換を介して出力を入力にリダイレクトできるファイルを生成できるためです>(sed 's/^/stdout: /')

別のプロセス交換内にプロセス交換を含める方法を説明するために、これを追加することを検討しました。読みやすくするためにいいえより高いレベルの埋め込みにプッシュします。しかし、何らかの理由で避ける必要がある場合管路そして関連シェル、これは良い解決策です。

関連情報