サブシェルを作成するためのBashルールを誤解しているようです。括弧は常にサブシェルを作成し、独自のプロセスとして実行されると思いました。
ただし、そうではないようです。コードスニペットA(下記)では、2番目のsleep
コマンドは別のシェルで実行されません(別のターミナルのpstree
によって決定されます)。ただし、コードスニペットBでは、2番目のsleep
コマンドは別のシェルで実行されます。スニペットの唯一の違いは、2番目のスニペットの括弧内に2つのコマンドがあることです。
誰かがサブシェルが作成されるときのルールを説明してもらえますか?
コードスニペットA:
sleep 5
(
sleep 5
)
コードスニペットB:
sleep 5
(
x=1
sleep 5
)
括弧は常にサブシェルを開始します。何が起こっているかというと、bashがsleep 5
はそのサブシェルによって実行される最後のコマンドなので、 exec
+ fork
の代わりにexec
を呼び出します。 sleep
コマンドは、同じプロセスのサブシェルを置き換えます。
つまり、基本ケースは次のとおりです。
( … )
サブシェルを作成します。元のプロセスはfork
およびwait
を呼び出します。サブシェルであるサブプロセス:sleep
は、サブプロセスのサブプロセスを必要とする外部コマンドです。サブシェルはfork
およびwait
を呼び出します。サブサブプロセス:exec
を実行します。exit
。wait
はサブシェルで完了します。wait
は元のプロセスで完了します。最適化は次のとおりです。
( … )
サブシェルを作成します。元のプロセスはfork
およびwait
を呼び出します。サブプロセスでは、exec
:を呼び出すまでのサブシェルです。sleep
は外部コマンドであり、このプロセスが実行する必要がある最後のことです。exec
を実行します。exit
。wait
は元のプロセスで完了します。sleep
の呼び出し後に何かを追加する場合、サブシェルを保持する必要があるため、この最適化は行われません。
sleep
の呼び出しの前に何か他のものを追加すると、最適化は行われ(kshはそれを行います)、bashはそれを行いません(この最適化では非常に保守的です)。
「一般的に、スクリプト内の外部コマンドはサブプロセスをフォークしますが、Bashビルトインはフォークしません。このため、ビルトインは、同等の外部コマンドよりも実行が速く、システムリソースの使用量も少なくなります。」
そしてもう少し下に:
「括弧内に埋め込まれたコマンドリストはサブシェルとして実行されます。」
例:
[root@talara test]# echo $BASHPID
10792
[root@talara test]# (echo $BASHPID)
4087
[root@talara test]# (echo $BASHPID)
4088
[root@talara test]# (echo $BASHPID)
4089
OPコードを使用した例(私はせっかちなのでスリープ時間が短い):
echo $BASHPID
sleep 2
(
echo $BASHPID
sleep 2
echo $BASHPID
)
出力:
[root@talara test]# bash sub_bash
6606
6608
6608
@ギレスの回答への追加のメモ。
Gillesによると:The parentheses always start a subshell.
ただし、そのようなサブシェルが持っている数は繰り返すかもしれません:
$ (echo "$BASHPID and $$"; sleep 1)
2033 and 31679
$ (echo "$BASHPID and $$"; sleep 1)
2040 and 31679
$ (echo "$BASHPID and $$"; sleep 1)
2047 and 31679
ご覧のとおり、$$は繰り返し続けていますが、これは期待どおりです(正しいman bash
行を見つけるには、このコマンドを実行してください)。
$ LESS=+/'^ *BASHPID' man bash
バッシュピッド
現在のbashプロセスのプロセスIDに展開します。これは、bashを再初期化する必要のないサブシェルなど、特定の状況下では$$とは異なります。
つまり、シェルが再初期化されない場合、$$は同じです。
またはこれで:
$ LESS=+/'^ *Special Parameters' man bash
特別なパラメータ
$シェルのプロセスIDに展開されます。 ()サブシェルでは、サブシェルではなく、現在のシェルのプロセスIDに展開されます。
$$
は、(サブシェルではなく)現在のシェルのIDです。