GNU/Linux >> Linux の 問題 >  >> Linux

プロセス置換の出力が正常ではありませんか?

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の方法の説明付き) ボンネットの下でそれを行います。

承認された回答:

はい、bashkshのように (機能が由来する場所)、プロセス置換内のプロセスは待機されません(スクリプト内の次のコマンドを実行する前に)。

<(...)の場合 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)を実行しても効果はありません )

rccmd1 >{cmd2}を使用 構文)、kshと同じ ただし、$apidsを使用してすべてのバックグラウンドプロセスのpidを取得できます。 。

escmd1 >{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と同じ回避策を使用します 。


Linux
  1. 親プロセスが終了したときの新しい親プロセス?

  2. 所有権を剥奪されてターミナルを失ったプロセスの出力はどうなりますか?

  3. 「最後の」コマンドの出力?

  1. 実行中のプロセスの出力リダイレクトを変更するにはどうすればよいですか?

  2. プロセス置換とパイプ?

  3. プロセスIDの最大値は?

  1. 別のBashセッションで実行中のプロセスの出力を表示する方法は?

  2. サブシェルの出力をプロセスにリダイレクトしますか?

  3. 標準出力を閉じる(>&-)?