括弧は本当にコマンドをサブシェルに配置するのでしょうか?

括弧は本当にコマンドをサブシェルに配置するのでしょうか?

私が読んだところによると、コマンドを括弧で囲むと、スクリプトを実行するのと同様に、サブシェルで実行されるはずです。これが本当なら、変数 x がエクスポートされていない場合、変数 x はどのようにして認識されるのでしょうか?

x=1

(echo $x)コマンドラインで実行すると1

echo $xスクリプトを実行すると、予想どおり何も起こりません

答え1

サブシェルは、元のシェルプロセスとほぼ同じコピーとして始まります。内部的には、シェルはforkシステム コール1は、コードとメモリがコピー2である新しいプロセスを作成します。サブシェルが作成されると、サブシェルとその親の間にはほとんど違いはありません。特に、同じ変数があります。$$特殊変数でさえ、サブシェルで同じ値を保持します。これは、元のシェルのプロセス ID です。同様に、$PPID元のシェルの親の PID です。

いくつかのシェルは、サブシェル内のいくつかの変数を変更します。Bash ≥4.0 はBASHPIDシェル プロセスの PID を設定しますが、これはサブシェルで変更されます。Bash、zsh、mksh は、$RANDOM親とサブシェルで異なる値を生成するように調整します。ただし、このような組み込みの特殊なケースを除いて、すべての変数はサブシェルでも元のシェルと同じ値、同じエクスポート ステータス、同じ読み取り専用ステータスなどを持ちます。すべての関数定義、エイリアス定義、シェル オプション、およびその他の設定も継承されます。

によって作成されたサブシェルは、(…)作成者と同じファイル記述子を持ちます。サブシェルを作成する他の手段では、ユーザーコードを実行する前に一部のファイル記述子が変更されます。たとえば、パイプの左側は、標準出力がパイプに接続されたサブシェル3で実行されます。サブシェルは、同じ現在のディレクトリ、同じシグナルマスクなどで開始されます。数少ない例外の 1 つは、サブシェルがカスタムトラップを継承しないことです。無視されたシグナル ( ) はサブシェルで無視されたままですが、他のトラップ (trap '' SIGNALtrap CODE信号)はデフォルトのアクション4にリセットされます。

サブシェルはスクリプトの実行とは異なります。スクリプトは別のプログラムです。この別のプログラムは、偶然にも親と同じインタプリタによって実行されるスクリプトである可能性がありますが、この偶然によって別のプログラムが親の内部データに対して特別な可視性を持つことはありません。エクスポートされていない変数は内部データであるため、子シェルスクリプトのインタプリタが処刑された、これらの変数は表示されません。エクスポートされた変数、つまり環境変数は、実行されたプログラムに送信されます。

したがって:

x=1
(echo $x)

1サブシェルはそれを生成したシェルの複製であるため、出力されます。

x=1
sh -c 'echo $x'

シェルをシェルの子プロセスとして実行しますが、x2行目のはx2行目の と何ら関係がありません。

x=1
perl -le 'print $x'

または

x=1
python -c 'print x'

1 シェルがフォークを最適化しない限り、実行中のコードの動作を維持するために必要なだけフォークをエミュレートします。Ksh93は多くの最適化を行いますが、他のシェルはほとんどそうではありません。
2 意味的には、それらはコピーです。実装の観点からは、多くの共有が行われています。
3 右側については、シェルによって異なります。
4 これをテストする場合は、次のようなもの$(trap)元のシェルのトラップを報告する場合があります。また、多くのシェルではトラップに関する稀なケースでバグが発生することに注意してください。たとえば、ニンジャルbash 4.3 では、「2 つのサブシェル」の場合にネストされたサブシェルからのトラップbash -x -c 'trap "echo ERR at \$BASH_SUBSHELL \$BASHPID" ERR; set -E; false; echo one subshell; (false); echo two subshells; ( (false) )'は実行されますが、中間のサブシェルからのトラップは実行されないことに注意してください。オプションはトラップをすべてのサブシェルに伝播する必要がありますが、中間のサブシェルは最適化されているため、そのトラップを実行するために存在しません。ERRERRset -EERRERR

答え2

明らかに、すべてのドキュメントに記載されているように、括弧で囲まれたコマンドはサブシェルで実行されます。

サブシェルは親のすべての変数のコピーを継承します。違いは、サブシェルで行った変更は親には反映されないことです。

ksh のマニュアル ページでは、bash のマニュアル ページよりも少しわかりやすく説明されています。

man ksh:

括弧で囲まれたコマンドは、エクスポートされていない変数を削除せずにサブシェルで実行されます。

man bash:

(リスト)

list はサブシェル環境で実行されます (以下のコマンド実行環境を参照)。シェルの環境に影響する変数の割り当てと組み込みコマンドは、コマンドが完了した後は有効ではなくなります。

コマンド実行環境

シェルには実行環境があり、次の要素から構成されます: [...] 変数割り当てによって設定されるシェル パラメータ [...]。
コマンド置換、括弧でグループ化されたコマンド、および非同期コマンドは、シェル環境の複製であるサブシェル環境で呼び出されます [...]

関連情報