POSIX用語では、サブシェル環境はシェル実行環境の概念にリンクされています。
サブシェル環境は、親環境の複製として作成された別個のシェル実行環境です。その実行環境には、開かれたファイル、umask、作業ディレクトリ、シェル変数/関数/エイリアスなどが含まれます...
そのサブシェル環境を変更しても、親環境には影響しません。
従来、POSIX仕様のベースとなっているBourneシェルまたはksh88で、子プロセスをフォークすることによって行われていました。
POSIXがサブシェル環境でコマンドを実行することを要求または許可する領域は、伝統的にksh88が子シェルプロセスをforkした領域です。
ただし、実装に子プロセスを使用するよう強制することはありません。
代わりに、シェルはその個別の実行環境を好きなように実装することを選択できます。
たとえば、ksh93は、親の実行環境の属性を保存し、分岐を回避できるコンテキストでサブシェル環境の終了時にそれらを復元することでそれを行います(ほとんどのシステムでは分岐としての最適化が非常に高価です)。
たとえば、次の場所にあります。
cd /foo; pwd
(cd /bar; pwd)
pwd
POSIXではcd /foo
、別の環境で実行する必要があり、次のような出力が必要です。
/foo
/bar
/foo
別のプロセスで実行する必要はありません。たとえば、stdoutが壊れたパイプにpwd
なった場合、サブシェル環境で実行すると、SIGPIPEが唯一のシェルプロセスに送信されます。
を含むほとんどのシェルは、子プロセスbash
内のコードを評価することで実装し(...)
ます(親プロセスは終了を待機します)が、ksh93は代わりに(...)
、すべて同じプロセス内でコードを実行すると、
- サブシェル環境であることに注意してください。
- で
cd
、以前の作業ディレクトリ(通常はO_CLOEXECで開かれたファイル記述子に)を保存し、OLDPWD、PWD変数、およびcd
変更する可能性のあるすべての値を保存してから、chdir("/bar")
- サブシェルから戻ると、現在の作業ディレクトリが復元され(
fchdir()
保存されたfd上に)、サブシェルが変更した可能性のあるすべてのものが復元されます。
子プロセスを回避できない状況があります。ksh93はフォークしません:
var=$(subshell)
(subshell)
しかし、
{ subshell; } &
{ subshell; } | other command
つまり、物事を別々のプロセスで実行して、同時に実行できるようにする必要がある場合です。
ksh93の最適化はそれよりもさらに進んでいます。たとえば、
var=$(pwd)
ほとんどのシェルはプロセスを分岐し、子にpwd
パイプにリダイレクトされたstdoutを使用してコマンドを実行させpwd
、現在の作業ディレクトリをそのパイプに書き込み、親プロセスはパイプの反対側で結果を読み取り、ksh93
どちらも仮想化しないフォークもパイプも必要ありません。フォークとパイプは、非組み込みコマンドにのみ使用されます。
シェルが子プロセスをフォークするサブシェル以外のコンテキストがあることに注意してください。たとえば、別の実行可能ファイル(同じシェルインタープリター用のスクリプトではない)に格納されているコマンドを実行するには、シェルはプロセスをフォークして、そのコマンドを実行する必要があります。そのコマンドが戻った後、さらにコマンドを実行できます。
に:
/bin/echo "$((n += 1))"
これはサブシェルではなく、コマンドは現在のシェル実行環境で評価され、現在のシェル実行環境のn
変数がインクリメントされますが、シェルは子プロセスをフォークして、引数としてを/bin/echo
展開してそのコマンドを実行し$((n += 1))
ます。
多くのシェルは、スクリプトまたはサブシェル(子プロセスとして実装されるサブシェルの場合)の最後のコマンドである場合、その外部コマンドを実行するために子プロセスをフォークしないという最適化を実装します。(bash
ただし、そのコマンドがサブシェルの唯一のコマンドである場合にのみ実行されます)。
つまり、これらのシェルでは、サブシェルの最後のコマンドが外部コマンドである場合、サブシェルによって余分なプロセスが生成されることはありません。比較すると:
a=1; /bin/echo "$a"; a=2; /bin/echo "$a"
と
a=1; /bin/echo "$a"; (a=2; /bin/echo "$a")
同じ数のプロセスが作成されますが、2番目の場合のみ、2番目のフォークがより早くa=2
実行され、サブシェル環境で実行されます。