echo one; echo two > >(cat); echo three;
コマンドは予期しない出力を出します。
私はこれを読みました:プロセス置換はbashでどのように実装されていますか?インターネット上のプロセス置換に関する他の多くの記事ですが、なぜこのように動作するのか理解できません。
期待される出力:
one
two
three
実際の出力:
prompt$ echo one; echo two > >(cat); echo three;
one
three
prompt$ two
また、この2つのコマンドは、私の観点からは同等であるはずですが、そうではありません。
##### first command - the pipe is used.
prompt$ seq 1 5 | cat
1
2
3
4
5
##### second command - the process substitution and redirection are used.
prompt$ seq 1 5 > >(cat)
prompt$ 1
2
3
4
5
なぜ私は、それらは同じでなければならないと思いますか?なぜなら、両方がseq
を接続するからです cat
への出力 匿名パイプを介した入力–ウィキペディア、プロセス置換。
質問: なぜこのように動作するのですか?私のエラーはどこにありますか?包括的な答えが望まれます(bash
の方法の説明付き) ボンネットの下でそれを行います。
承認された回答:
はい、bash
で ksh
のように (機能が由来する場所)、プロセス置換内のプロセスは待機されません(スクリプト内の次のコマンドを実行する前に)。
<(...)
の場合 1つは、通常、次のように問題ありません。
cmd1 <(cmd2)
シェルはcmd1
を待機します およびcmd1
通常、cmd2
を待機します 置換されるパイプのファイルの終わりまで読み取るため、通常、ファイルの終わりはcmd2
のときに発生します。 死ぬ。これは、いくつかのシェル(bash
ではない)と同じ理由です。 )cmd2
をわざわざ待たないでください cmd2 | cmd1
。
cmd1 >(cmd2)
の場合 ただし、cmd2
の方が多いため、通常はそうではありません。 通常、cmd1
を待機します そこにあるので、通常は後に終了します。
これはzsh
で修正されています cmd2
を待機します そこに(ただし、cmd1 > >(cmd2)
と記述した場合はそうではありません およびcmd1
組み込みではない場合は、{cmd1} > >(cmd2)
を使用してください 代わりに文書化されています。
ksh
デフォルトでは待機しませんが、wait
で待機できます 組み込み(pidを$!
で使用できるようにします 、ただし、cmd1 >(cmd2) >(cmd3)
を実行しても効果はありません )
rc
(cmd1 >{cmd2}
を使用 構文)、ksh
と同じ ただし、$apids
を使用してすべてのバックグラウンドプロセスのpidを取得できます。 。
es
(cmd1 >{cmd2}
も使用 )cmd2
を待ちます zsh
のように 、またcmd2
を待ちます <{cmd2}
で プロセスのリダイレクト。
bash
cmd2
のpidを作成します (または、より正確には、cmd2
を実行するサブシェルです。 $!
で利用可能な、そのサブシェルの子プロセスで、最後のコマンドですが) 、しかしそれを待つことはできません。
bash
を使用する必要がある場合 、次のコマンドで両方のコマンドを待機するコマンドを使用して、問題を回避できます。
{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1
これにより、両方のcmd1
およびcmd2
fd3をパイプに開いてもらいます。 cat
もう一方の端でファイルの終わりを待つため、通常は両方のcmd1
の場合にのみ終了します およびcmd2
死んでいます。そして、シェルはそのcat
を待ちます 指図。これは、すべてのバックグラウンドプロセスの終了をキャッチするためのネットとして見ることができます(&
のように、バックグラウンドで開始された他のものに使用できます。 、coprocs、またはデーモンが通常行うようにすべてのファイル記述子を閉じない限り、バックグラウンドで動作するコマンドもあります。
上記の無駄なサブシェルプロセスのおかげで、cmd2
でも機能することに注意してください。 fd 3を閉じます(コマンドは通常それを行いませんが、sudo
のようなものもあります またはssh
行う)。 bash
の将来のバージョン 最終的には、他のシェルのようにそこで最適化を行う可能性があります。次に、次のようなものが必要になります:
{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1
そのsudo
を待機しているそのfd3が開いている追加のシェルプロセスがまだあることを確認するため コマンド。
cat
に注意してください 何も読み取りません(プロセスはfd 3に書き込みを行わないため)。同期のためだけにあります。 read()
を1つだけ実行します 最後に何も返さないシステムコール。
cat
の実行を実際に回避できます コマンド置換を使用してパイプ同期を実行する:
{ unused=$( { cmd1 >(cmd2); } 3>&1 >&4 4>&-); } 4>&1
今回は、cat
ではなくシェルです これは、cmd1
のfd3でもう一方の端が開いているパイプから読み取っています。 およびcmd2
。変数の割り当てを使用しているため、cmd1
の終了ステータス $?
で入手できます 。
または、プロセス置換を手動で実行してから、システムのsh
を使用することもできます。 標準のシェル構文になるため:
{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1
ただし、前述のように、すべてのsh
ではないことに注意してください。 実装はcmd1
を待ちます cmd2
の後 終了しました(ただし、それは他の方法よりも優れています)。その時、$?
cmd2
の終了ステータスが含まれます; bash
およびzsh
cmd1
を作成します の終了ステータスは${PIPESTATUS[0]}
で確認できます および$pipestatus[1]
それぞれ(pipefail
も参照してください いくつかのシェルのオプションなので、$?
最後以外のパイプコンポーネントの障害を報告できます)
yash
に注意してください プロセスリダイレクトにも同様の問題があります 特徴。 cmd1 >(cmd2)
cmd1 /dev/fd/3 3>(cmd2)
と記述されます そこの。しかし、cmd2
待機しておらず、wait
を使用できません それを待つために、そのpidは$!
で利用可能になりません どちらか変数。 bash
と同じ回避策を使用します 。