プロセス交換出力が故障しました。

プロセス交換出力が故障しました。

これ

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匿名パイプを介して出力を入力に接続するため -catWikipedia、プロセスの交換

質問:なぜこれですか?私の間違いはどこにありますか?包括的な答えが必要です(そしてbashそれが後ろでどのように機能するかについての説明)。

ベストアンサー1

はい、bash(この機能のソース)と同様に、kshプロセスの交換内でプロセスを待ちません(スクリプトで次のコマンドを実行する前)。

一般的に、次のような人には大丈夫です<(...)

cmd1 <(cmd2)

シェルは、交換されるパイプのファイルが終了するのを待ち、通常読み取るのを待ち、通常ファイルの終わりは死亡時にcmd1発生します。これは、複数のシェル(ではなく)が待機しない理由でもあります。cmd1cmd2cmd2bashcmd2cmd2 | cmd1

しかし、cmd1 >(cmd2)通常の場合にはそうではありません。通常はそこで待機し、通常はcmd2後でcmd1終了するからです。

これは修正されました。zshお待ちくださいcmd2(ただしasを書いて、cmd1 > >(cmd2)組み込みcmd1機能ではない場合は{cmd1} > >(cmd2)代わりにasを使用してください)録音された)。

ksh基本的には待ちませんが、組み込み機能を使って待つことができます。wait(また、pidを使用できるようにしてくれますが、$!そうすると役に立ちませんcmd1 >(cmd2) >(cmd3)。)

rc(構文を使用)、を使用してすべてのバックグラウンドプロセスのpidを取得できることを除いて、他のものとcmd1 >{cmd2}同じです。ksh$apids

es(また同様) in cmd1 >{cmd2}waitであり、また進行中のリダイレクトを待っています。cmd2zshcmd2<{cmd2}

bashは、pid cmd2(またはより正確にはサブシェルのpid、それがcmd2最後のコマンドであってもそのサブシェルのサブプロセスで実行されるため)を使用できるようにしますが、$!ユーザーがそれを待つことを許可しません。

必要に応じて、bash次の2つのコマンドを待つコマンドを使用して問題を解決できます。

{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1

これにより、fd3とfd3のcmd1両方がcmd2パイプで開かれます。もう一方の端でファイルが終了するのを待つため、通常は両方が発生し、終了したcatときにのみcmd1終了しますcmd2。シェルはコマンドを待ちますcat。すべてのバックグラウンドプロセスの終了をキャプチャするネットワークと考えることができます。 (&デーモンですべて終了しない限り、バックグラウンド自体の、coprocsやコマンドなど、バックグラウンドで開始された他の操作に使用できます。)通常、ファイル記述子を実行します)。

上記の無駄なサブシェルプロセスのため、cmd2fd 3が閉じられてもまだ機能します(コマンドは通常これを実行しませんが、いくつかのコマンドは同様または実行しますsudossh。将来のバージョンはbash最終的に他のシェルのように最適化される可能性があります。その後、次のようなものが必要です。

{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1

コマンドを待っているfd 3を持つ追加のシェルプロセスがまだ開いていることを確認してくださいsudo

cat何も読まないことに注意してください(プロセスはfd 3に書かないからです)。まさに同期のためです。システムコールのみを実行しread()、最終的には何も返しません。

catパイプライン同期のコマンド置換を使用すると、実際にこの実行を回避できます。

{ unused=$( { cmd1 >(cmd2); } 3>&1 >&4 4>&-); } 4>&1

今回は、シェルがfd 3の反対側の端にcat開いているパイプからデータを読み込みます。変数割り当てを使用するので、終了ステータスを で使用できます。cmd1cmd2cmd1$?

または、プロセス置換を手動で実行してから、システムの置換を使用することもできます。shこれは標準シェル構文になります。

{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1

しかし、前述のように、すべての実装が完了するのをsh待つわけではありません(その逆の場合よりも優れています)。この時点では、make と make の終了状態はそれぞれおよびで使用できますが、終了状態が含まれます (最後のコンポーネント以外のパイプラインコンポーネントエラーを報告できるように、一部のシェルのオプションも参照)。cmd1cmd2$?cmd2bashzshcmd1${PIPESTATUS[0]}$pipestatus[1]pipefail$?

yashプロセスにも同様の問題があります。リダイレクト特徴。そこにcmd1 >(cmd2)記録されますcmd1 /dev/fd/3 3>(cmd2)。ただし、待たないと待機することはできず、cmd2そのpidは変数では使用できません。と同じ回避策を使用します。wait$!bash

おすすめ記事