
これ
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
Wikipedia、プロセスの交換。
質問:なぜこれですか?私の間違いはどこにありますか?包括的な答えが必要です(そしてbash
それが後ろでどのように機能するかについての説明)。
答え1
はい、bash
(この機能のソース)と同様に、ksh
プロセスの交換内でプロセスを待ちません(スクリプトで次のコマンドを実行する前)。
一般的に、次のような人には大丈夫です<(...)
。
cmd1 <(cmd2)
シェルは、交換されるパイプのファイルが終了するのを待ち、通常読み取るのを待ち、通常ファイルの終わりは死亡時にcmd1
発生します。これは、複数のシェル(ではなく)が待機しない理由でもあります。cmd1
cmd2
cmd2
bash
cmd2
cmd2 | 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であり、また進行中のリダイレクトを待っています。cmd2
zsh
cmd2
<{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やコマンドなど、バックグラウンドで開始された他の操作に使用できます。)通常、ファイル記述子を実行します)。
上記の無駄なサブシェルプロセスのため、cmd2
fd 3が閉じられてもまだ機能します(コマンドは通常これを実行しませんが、いくつかのコマンドは同様または実行しますsudo
)ssh
。将来のバージョンは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
開いているパイプからデータを読み込みます。変数割り当てを使用するので、終了ステータスを で使用できます。cmd1
cmd2
cmd1
$?
または、プロセス置換を手動で実行してから、システムの置換を使用することもできます。sh
これは標準シェル構文になります。
{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1
しかし、前述のように、すべての実装が完了するのをsh
待つわけではありません(その逆の場合よりも優れています)。この時点では、make と make の終了状態はそれぞれおよびで使用できますが、終了状態が含まれます (最後のコンポーネント以外のパイプラインコンポーネントエラーを報告できるように、一部のシェルのオプションも参照)。cmd1
cmd2
$?
cmd2
bash
zsh
cmd1
${PIPESTATUS[0]}
$pipestatus[1]
pipefail
$?
yash
プロセスにも同様の問題があります。リダイレクト特徴。そこにcmd1 >(cmd2)
記録されますcmd1 /dev/fd/3 3>(cmd2)
。ただし、待たないと待機することはできず、cmd2
そのpidは変数では使用できません。と同じ回避策を使用します。wait
$!
bash
答え2
2 番目のコマンドを別のコマンドにパイプして、cat
入力パイプが閉じるまで待つことができます。前任者:
prompt$ echo one; echo two > >(cat) | cat; echo three;
one
two
three
prompt$
短くて簡単です。
=========
簡単に見えるかもしれませんが、その裏面には多くのことが起こっています。仕組みに興味がない場合は、残りの回答を無視できます。
これを行うと、echo two > >(cat); echo three
インタラクティブ>(cat)
シェルから分岐して独立して実行されますecho two
。したがって、echo two
完了してecho three
実行します。ただし、>(cat)
完了する前に実行してください。予期しない時間(数ミリ秒後)にデータをインポートするときにbash
端末に戻るために改行を押す必要があるプロンプトのようなものを提供します(たとえば、他のユーザーがメッセージを送信した場合など)。>(cat)
mesg
ただし、与えられた場合、echo two > >(cat) | cat; echo three
2つのサブシェルが作成されます(記号文書に従って|
)。
Aというサブシェルはのもの、echo two > >(cat)
Bというサブシェルはのものですcat
。 Aは自動的にBに接続されます(Aの標準出力はBの標準入力です)。echo two
その後、>(cat)
実行が開始されます。>(cat)
stdout は B の stdin と同じ A の stdout に設定されます。完了するとecho two
A が終了し、標準出力が閉じます。しかし、>(cat)
Bのstdinへの参照は残ります。 2番目cat
のstdinはBのstdinを保持し、cat
EOFが見えるまで終了しません。 EOFは、誰も書き込みモードでファイルを開かない場合にのみ提供されるため、>(cat)
stdoutは2番目のファイルをブロックしますcat
。 Bはまだその瞬間を待っているcat
。echo two
終了すると>(cat)
最終的にEOFが得られるので、>(cat)
バッファをフラッシュして終了します。もはやB's / secondcat
の標準入力を持っている人がいないので、2番目のものはcat
EOFを読みます(Bは標準入力をまったく読みませんので気にしません)。このEOFは、2番目のエントリがcat
バッファをフラッシュし、stdoutを閉じてから終了するようにします。その後、Bはcat
すでに終了しており、Bは待っていたため終了しますcat
。
>(cat)
bashは!のサブシェルも作成することに注意してください。このため、あなたは見るでしょう
echo two > >(sleep 5) | cat; echo three
echo three
Bの標準入力が保存されていなくても、sleep 5
まだ実行される前に5秒待ちます。これは、Cが生成された隠しサブシェルを>(sleep 5)
待っており、sleep
CがBの標準入力を保持しているためです。あなたは方法を見ることができます
echo two > >(exec sleep 5) | cat; echo three
sleep
しかし、Bの標準入力は保存されず、Bの標準入力を格納するゴーストサブシェルCがないため、待ち時間はありません(execはCを分岐して待つのではなく、スリープがCを置き換えるように強制しますsleep
)。この警告にもかかわらず、
echo two > >(exec cat) | cat; echo three
前述のように、関数は引き続き順番に正しく実行されます。