web-dev-qa-db-ja.com

なぜ `... | sed's / ^ / stdout:/ '`` ... >>(sed's / ^ / stdout:/') `がない場合、空のstdinに出力しますか?

私は機能的に同等であると私が思ったこれらの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の権利を理解した場合、サブシェル_( echo foo >&2 )_がstdoutに出力されない限り、>(sed 's/^/stdout: /')は実行されないことを彼は提案します。ただし、次のことを意味します。

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

bazを表示しないでください。

編集2:おそらく興味深いことですが、sedはパイプされた場合でも空の入力で_stdout:_を出力しません:

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

最初のコマンド、

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

簡略化された形式(一時ファイルを使用してechoによって生成されたデータを保持する):

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

つまり、最初のsedechoから標準エラーで生成されたものを読み取り、標準出力に書き込み、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は出力を生成しますが、標準出力出力を取得する他の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

つまり、2番目のコマンドの2番目のsedは何も読み取りません。特に、最初のコマンドのように最初のsedからの出力を読み取りません。


非常に単純化されたシンボルを使用して、最初のコマンドは次のようなものです

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

ここで、something1は、something2によって読み取られる標準出力に書き込みます。

同じ表記を使用する2番目のコマンド、

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

つまり、something1something2は互いに接続されておらず、something2something1が生成しているものを決して読み取ることができません。さらに、utility-writing-to-stderrは標準出力ストリームで何も生成しないため、something2は標準入力から読み取るものがありません。

3
Kusalananda

シェルでのリダイレクトの順序のため、_>_は_( 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
_
1
user1133275

2つのコマンドは同等ではありません。

コマンド(1):

_( one ) 2> >(two) | three 
_

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

コマンド(2):

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

oneの(std)出力をthreeに送信します(stderrはtwoに移動します)。


痛みを伴う詳細は次のとおりです。

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

  • コマンドthreeが開始され(_sed 's/^/stdout: /'_)、stdinへの入力を待機しています。
  • _2_(stderr)からstdintwoへのリダイレクトが行われます。
  • oneの内部リダイレクトが行われます(_echo foo >&2_)。
  • コマンドはfoostderrに送信して実行されます(標準出力には何もありません)。
  • Word fooは(stderr of one)から(stdin of two)にリダイレクトされます。
  • 2つのコマンドが実行されます(_sed 's/^/stderr: /'_)
  • 変更されたtwo(_stderr: foo_)の出力は、twoのstdoutに送信されます。
  • 現在のtwoの標準出力は、パイプを介してthreeに送られます。
  • 最初から入力を待っていたコマンドthreeが出力を取得します。
  • 出力は、先頭に_stdout:_を追加して変更されます。
  • 最後の文字列_stdout: stderr: foo_はttyに送られます。

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

  • 最後のリダイレクト(_>_)が最初にビルドされます。
  • コマンドthreeは、stdinのみ>())で入力を待機して開始されます。
  • コマンドthreeのみoneの標準出力を取得します。
  • コマンドtwoが入力待ちで開始されます。
  • コマンドtwoは、oneのstderrを取得します(only)。
  • 1の内部リダイレクトは、oneのstdoutをそのstderrに接続します。
  • コマンドoneが実行され、fooが(stderrの)oneに送信されます。
  • footwoに移動します
  • two受信した文字列を_stderr: foo_に変更し、それをttyに送信します。
  • threeは、oneのstdoutから空の入力を受け取ります。
  • three prints nothing(処理する行がないため)。

このコマンドを理解してください:

_printf '' | sed 's/^/initial: /'
_

出力がありません(そしてそれを機能させる方法はありません)。


順序の変更:コマンド3:(one)>>(three)2>>(two)

演習として残されたtwoの出力をthreettyではない)の詳細に移動します。

1
Isaac

私は@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: /')にリダイレクトできます。

これを追加して、プロセス置換を別のプロセス置換に埋め込む方法を説明することを考えました。読みやすくするために、notより多くのレベルの埋め込みにプッシュします。しかし、何らかの理由でpipeおよび関連するShell、これは良い解決策です。

0
Cbhihe