web-dev-qa-db-ja.com

リダイレクトの一部である場合、 `>()`サブシェルのstdoutが異なるのはなぜですか(例: `>>()`)?

これは次の質問です: `... | sed 's/^/stdout:/'`が `...>>(sed 's/^/stdout:/ ') `はしませんか?

より具体的には、なぜこれがパイプなのか:

$ tee 2> >(readlink /proc/self/fd/1) < /dev/null | cat        
pipe:[17955449]

これが端末デバイスの場合:

$ tee >(readlink /proc/self/fd/1) < /dev/null | cat   
/dev/pts/31

前者の出力も、コマンドを入力しているシェルから継承された端末であると自然に思っていたでしょうが、bashとzshの両方が、コマンドの出力にリダイレクトするために明示的な手順を実行する必要があるようです。リダイレクトしています。なぜ彼らはそれをしているのですか?または何か他のことが起こっていますか?前者のサブシェルは、tee'ing execの前にteeのプロセスから生成されますか?一方のサブシェルは子プロセスから継承し、もう一方は親プロセスから継承しますか?どうやって?

うーん...タグを付けてbashをチェックしていると、どちらの場合もbashのパイプであることがわかります...つまり、これはzshの特性です。

5
JoL

zshは、同じコマンドの複数のリダイレクトをサポートします。たとえば、abc > def > ghiは、abcの完全な出力をdefghiの両方に入れます。また、>と1つの装飾されていない|リダイレクトの両方を一度に許可します。これは、最初の例で使用しているものです。

その複数のリダイレクトの状況では、それにリダイレクトを伴うプロセス置換

tee 2> >(readlink /proc/self/fd/1) < /dev/null | cat

メインコマンドと同様に出力がパイプ処理され、リダイレクトなしのプロセス置換が行われます。

tee >(readlink /proc/self/fd/1) < /dev/null | cat

そうではありません。パイプはある意味で優先されます。リダイレクトのブランチからのすべての出力は、パイプを通過します。この場合、2つのブランチ(メインコマンド自体とプロセス置換からの出力)があり、どちらもcatを通過するため、どちらも標準出力をパイプとして認識します。

Bashは常にcatに置換をパイプし、そもそもそのような複数のリダイレクトをサポートしていません。

本質的に、zshに複数のリダイレクトがある場合でも、|は1つしか存在できず、パイプはすべてのブランチに分散しますリダイレクトですが、プロセス置換自体はその一部ではありません->を使用した実際の出力リダイレクトのみ。

これはリダイレクトのプロパティであり、プロセス置換によって表示されます。リダイレクトされたプロセスとリダイレクトされていないプロセスの両方を一緒に使用すると、次のことがわかります。

$ true >( readlink /proc/self/fd/1 ) > >( readlink /proc/self/fd/1 ) | cat
/dev/tty1
pipe:[2975]

最初のもの(単なる置換)にはTTYがstdoutとしてあり、2番目(リダイレクト先)にはcatへのパイプがあります。これは、そのセットアップを構築する唯一の簡単な方法です。pipeed-toコマンドの単一インスタンスと、その前のpipeedコマンドとunpipedコマンドの混合です。望まないときに出力をリダイレクトする間違ったトラックになってしまった場合は、> /dev/pts/...で回復できますが、Bashのようにベア置換をリダイレクトしたい場合は、まだ運が悪いです。

プロセス置換はそれ自体でシェルから環境を継承しますが、リダイレクトはパイプ内の入力と出力の両方と要素を変更します。このように機能するのは必要ではないと思いますが、一貫したルールがあります:|は常に>に分散しますが、引数は無視します

実際の動作が何であるかを示す私の実験は、ここにインラインで含めるには長すぎて扱いにくいですが、 この回答の改訂履歴では です。以下に、4つの異なるケース(>|のそれぞれはい/いいえ)とそれらの動作を要約します。


ケース分析

全体として、zshの動作には4つの異なるケースがあります。

  1. |、いいえ>

    abc >( def ) | ghi
    

    これは、ベースコマンドの出力をパイプに、サブシェルの出力をTTYに送信し、abcへのパスを渡します。

  2. |なし、>なし

    abc >( def )
    

    これにより、すべてがTTYに送信され、パスがabcに渡されます。

  3. |はありませんが、>

    abc > >( def )
    

    これにより、ベースコマンドの出力はサブシェルにのみ送信され、サブシェルはTTYに送信されます。abcには引数を指定しません。

  4. |および>

    abc > >( def ) | ghi
    

    これは、ベースコマンドの出力をbothプロセス置換とパイプに送信し、サブシェルの出力をパイプに送信します。abcには引数を指定しません。 abc | tee >( def ) | ghiとして機能します。

変更がパイプから遠く離れているように感じる場合、ケース4がケース1とそれほど異なることはあまり好きではありませんが、それはそうです。

1
Michael Homer