中括弧を使用したBashサブシェルの作成


31

よれば、この中括弧の間のコマンドのリストを確定すると、リストは、現在のシェルのコンテキスト内で実行されるようにします。サブシェルは作成されません

psこれを実際に使用して見る

これは、コマンドラインで直接実行されるプロセスパイプラインのプロセス階層です。4398は、ログインシェルのPIDです。

sleep 2 | ps -H;
  PID TTY          TIME CMD
   4398 pts/23   00:00:00 bash
   29696 pts/23   00:00:00   sleep
   29697 pts/23   00:00:00   ps

コマンドラインで直接実行される中括弧間のプロセスパイプラインのプロセス階層に従います。4398は、ログインシェルのPIDです。これは、すべてが現在のシェルコンテキストで実行されることを証明する上記の階層に似ています

{ sleep 2 | ps -H; }
   PID TTY          TIME CMD
    4398 pts/23   00:00:00 bash
    29588 pts/23   00:00:00   sleep
    29589 pts/23   00:00:00   ps

これは、sleepパイプライン自体が中かっこ内に配置されている場合のプロセス階層です(つまり、2レベルのかっこ)

{ { sleep 2; } | ps -H; }
  PID TTY          TIME CMD
   4398 pts/23   00:00:00 bash
   29869 pts/23   00:00:00   bash
   29871 pts/23   00:00:00     sleep
   29870 pts/23   00:00:00   ps

ドキュメンテーションに中括弧間のコマンドが現在のシェルコンテキストで実行されると記載されている場合、3番目のケースでbash実行するサブシェルを作成する必要があるのはなぜsleepですか?


興味深いことに、3番目のケースでは内部グループがパイプラインの一部であり、したがって、たとえばパイプラインの一部である他の関数呼び出しのようにサブシェルで実行されるためだと思います。理にかなっていますか?
ミロスラフコシュカル14

2
「シェルはする必要があります」と言っているだけではありません...パイプラインはシェルコンテキストで実行されません。パイプラインが外部コマンドのみで構成されている場合、サブプロセスの作成で十分です。{ sleep 2 | command ps -H; }
ホークレイジング14

回答:


26

パイプラインでは、すべてのコマンドが(パイプで接続されたstdout / stdinを使用して)同時に実行されるため、異なるプロセスで実行されます。

cmd1 | cmd2 | cmd3

3つのコマンドはすべて異なるプロセスで実行されるため、少なくとも2つのコマンドは子プロセスで実行する必要があります。一部のシェルは、現在のシェルプロセスでそれらの1つを実行します(組み込みのように、readまたはパイプラインがスクリプトの最後のコマンドである場合)が、bashそれらをすべて独自の個別のプロセスで実行します(lastpipe最近のbashバージョンおよび特定の条件下でのオプションを除く))。

{...}グループコマンド。そのグループがパイプラインの一部である場合、単純なコマンドのように別のプロセスで実行する必要があります。

に:

{ a; b "$?"; } | c

評価するためのシェルa; b "$?"は別のプロセスである必要があるため、サブシェルが必要です。シェルはb、そのグループで実行される最後のコマンドであるため、フォークしないことで最適化できます。一部のシェルはそれを行いますが、明らかにそうではありませんbash


3つのコマンドはすべて異なるプロセスで実行されるため、少なくとも2つはサブシェルで実行する必要があります。」この場合、なぜサブシェルが必要なのですか?親シェルはツリープロセスを生成できませんか?または、「サブシェルで実行する必要があります」と言った場合、シェルがそれ自体をフォークし、各cmd1 cmd2およびcmd3を実行しますか?これを実行するbash -c "sleep 112345 | cat | cat "と、作成されたbashが1つだけ表示され、その後に3つの子がサブバッシをインターリーブすることなく表示されます。
ハカン馬場

a; b "$?"は独立したプロセスであると評価するためにシェルが必要です。したがって、サブシェルが必要です。」なぜそれを理解するためにサブシェルが必要なのですか?それを理解するには何が必要ですか?解析が必要だと思いますが、他に何が必要ですか?。親シェルは解析できませんa; b "$?"か?サブシェルの基本的な必要性は本当にありますか、それともbashの設計決定/実装でしょうか?
ハカン馬場

@HakanBaba、潜在的な混乱を避けるために、それを「子プロセス」に変更しました。
ステファンシャゼラス

1
@HakanBaba、解析は親で行われます(コードを読み取るプロセス、そのコードが渡されない限りインタープリターを実行したプロセスeval)が、評価(最初のコマンドを実行し、それを待って、2番目を実行します)パイプに接続されたstdoutを持つ子で行われます。
ステファンシャゼラス

{ sleep 2 | ps -H; }、親のbash見てsleep 2それがフォーク/ execのが必要です。しかし{ { sleep 2; } | ps -H; }、親のbashには{ sleep 2; }、言い換えれば、bashコードがあります。親はfork / execを処理できるように見えますsleep 2が、新しいbashを再帰的に生成して、検出されたbashコードを処理します。それは私の理解です、それは理にかなっていますか?
ハカン馬場

19

中括弧を入れ子にすることは、新しいサブシェルを呼び出す必要がある追加のレベルのスコープを作成していることを示しているように見えます。この効果は、ps -H出力のBashの2番目のコピーで確認できます。

中括弧の最初のレベルで規定されているプロセスのみが、元のBashシェルのスコープ内で実行されます。ネストされた中括弧は、独自のスコープ付きBashシェルで実行されます。

$ { { { sleep 20; } | sleep 20; } | ps -H; }
  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5012 pts/1    00:00:00   bash
 5014 pts/1    00:00:00     bash
 5016 pts/1    00:00:00       sleep
 5015 pts/1    00:00:00     sleep
 5013 pts/1    00:00:00   ps

撮影| ps -Hミックスのアウトだけので、我々は、ネストされた中括弧を見ることができ、我々は実行することができps auxf | less、別のシェルで。

saml     29190  0.0  0.0 117056  3004 pts/1    Ss   13:39   0:00  \_ bash
saml      5191  0.0  0.0 117056  2336 pts/1    S+   14:42   0:00  |   \_ bash
saml      5193  0.0  0.0 107892   512 pts/1    S+   14:42   0:00  |   |   \_ sleep 20
saml      5192  0.0  0.0 107892   508 pts/1    S+   14:42   0:00  |   \_ sleep 20
saml      5068  0.2  0.0 116824  3416 pts/6    Ss   14:42   0:00  \_ bash
saml      5195  0.0  0.0 115020  1272 pts/6    R+   14:42   0:00      \_ ps auxf
saml      5196  0.0  0.0 110244   880 pts/6    S+   14:42   0:00      \_ less

しかし、もっと待ってください!

ただし、パイプを取り出してこの形式のコマンドを使用すると、実際に期待するものがわかります。

$ { { { sleep 10; } ; { sleep 10; } ; sleep 10; } } | watch "ps -H"

結果のウォッチウィンドウで、2秒ごとに更新が行われます:

これが最初sleep 10です:

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5678 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5681 pts/1    00:00:00     watch
 5682 pts/1    00:00:00       ps

2番目は次のsleep 10とおりです。

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5691 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5694 pts/1    00:00:00     watch
 5695 pts/1    00:00:00       ps

3番目は次のsleep 10とおりです。

  PID TTY          TIME CMD
29190 pts/1    00:00:00 bash
 5676 pts/1    00:00:00   bash
 5704 pts/1    00:00:00     sleep
 5677 pts/1    00:00:00   watch
 5710 pts/1    00:00:00     watch
 5711 pts/1    00:00:00       ps

中括弧の異なるネストレベルで呼び出された3つのスリープはすべて、実際にはBashのPID 5676内にとどまることに注意してください。だから私はあなたの問題は自己の使用を負っていると信じています| ps -H

結論

| ps -H(つまりパイプ)を使用すると、追加のサブシェルが発生するため、何が起こっているのかを調べる際にそのメソッドを使用しないでください。


そのため、「中括弧の最初のレベルで規定されているプロセスのみが、元のBashシェルのスコープ内で実行されます。」
xealits 14年

@xealits-それはフォローアップQですか?
slm

@slmは、私が見たように、答えの要点を強調しているだけです。中括弧の最初のレベルのコマンドは現在のシェルで実行され、ネストされた中括弧は新しいシェルを作成します。最初のレベルでは、サブシェルをすぐに作成する点で括弧が異なります。私がそれを間違えた場合-私を修正してください。しかし、他の人が指摘するように、最初の質問にはパイプラインもあります。したがって、個別のプロセスの作成。また、別のプロセスに使用する場合は、中括弧を使用してシェルを作成する必要があります。したがって、おそらく、問題の動作の理由です。
xealits 14

今、あなたの投稿を再読した後、私は間違っていたことがわかります-ネストステートメントはパイプラインの場合のみに関係します。したがって、中括弧は、別のプロセスをラップしない限り、新しいシェルを作成することはありません。
xealits 14

@xealits-それは正しいです。
slm

7

テストの結果を投稿します。これは、bashがパイプラインの一部である場合にのみ、グループコマンドのサブシェルを作成するという結論に導きます。サブシェルで。

$ { A=1; { A=2; sleep 2; } ; echo $A; }
2

$ { A=1; { A=2; sleep 2; } | sleep 1; echo $A; }
1

私のAもこれを示しています。
slm
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.