中かっこを使用して Bash サブシェルを作成する

中かっこを使用して Bash サブシェルを作成する

によると:

3.2.5.3 グループ化コマンド

{}

{ list; }

中かっこの間にコマンドリストを配置すると、そのリストは現在のシェルのコンテキストで実行されます。サブシェルは作成されません。

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

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

答え1

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

存在する

cmd1 | cmd2 | cmd3

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

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

存在する:

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

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

答え2

ネストされた中括弧は、新しいサブシェルを呼び出す必要がある追加のレベルの範囲を生成することを示すようです。 Bashの2番目のコピーを使用して、出力でこの効果を確認できますps -H

中かっこの最初のレベルで指定されたプロセスのみが、元の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と追加のサブシェルが生成されるため、何が起こっているのかを尋ねるときにその方法を使用しないでください。

答え3

私はテスト結果を公開します。これにより、bashは次の場合にグループコマンドのサブシェルを生成すると結論付けます。ただパイプラインの一部である場合、サブシェルでも呼び出される関数を呼び出すのと同じです。

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

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

関連情報