web-dev-qa-db-ja.com

バッシュでサブシェルを呼び出すためのルール?

サブシェルを作成するためのBashルールを誤解しているようです。括弧は常にサブシェルを作成し、独自のプロセスとして実行されると思いました。

ただし、そうではないようです。コードスニペットA(下記)では、2番目のsleepコマンドは別のシェルで実行されません(別のターミナルのpstreeによって決定されます)。ただし、コードスニペットBでは、2番目のsleepコマンドは別のシェルで実行されます。スニペットの唯一の違いは、2番目のスニペットの括弧内に2つのコマンドがあることです。

誰かがサブシェルが作成されるときのルールを説明してもらえますか?

コードスニペットA:

sleep 5
(
sleep 5
)

コードスニペットB:

sleep 5
(
x=1
sleep 5
)
26
bashful

括弧は常にサブシェルを開始します。何が起こっているかというと、bashがsleep 5はそのサブシェルによって実行される最後のコマンドなので、 exec + fork の代わりにexecを呼び出します。 sleepコマンドは、同じプロセスのサブシェルを置き換えます。

つまり、基本ケースは次のとおりです。

  1. ( … )サブシェルを作成します。元のプロセスはforkおよびwaitを呼び出します。サブシェルであるサブプロセス:
    1. sleepは、サブプロセスのサブプロセスを必要とする外部コマンドです。サブシェルはforkおよびwaitを呼び出します。サブサブプロセス:
      1. サブサブプロセスは、外部コマンド→execを実行します。
      2. 最終的にコマンドは終了します→exit
    2. waitはサブシェルで完了します。
  2. waitは元のプロセスで完了します。

最適化は次のとおりです。

  1. ( … )サブシェルを作成します。元のプロセスはforkおよびwaitを呼び出します。サブプロセスでは、exec:を呼び出すまでのサブシェルです。
    1. sleepは外部コマンドであり、このプロセスが実行する必要がある最後のことです。
    2. サブプロセスは外部コマンド→execを実行します。
    3. 最終的にコマンドは終了します→exit
  2. waitは元のプロセスで完了します。

sleepの呼び出し後に何かを追加する場合、サブシェルを保持する必要があるため、この最適化は行われません。

sleepの呼び出しの前に何か他のものを追加すると、最適化は行われ(kshはそれを行います)、bashはそれを行いません(この最適化では非常に保守的です)。

高度な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
4
Tim

@ギレスの回答への追加のメモ。

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です。

4
user79743