私が読んだことから、コマンドを括弧で囲むと、スクリプトの実行と同様に、サブシェルでコマンドを実行する必要があります。これが当てはまる場合、xがエクスポートされていない場合、変数xはどのように表示されますか?
x=1
(echo $x)
を実行しています コマンドラインで1
echo $x
を実行しています スクリプトでは、期待どおりに何も起こりません
承認された回答:
サブシェルは、元のシェルプロセスのほぼ同一のコピーとして始まります。内部では、シェルはfork
を呼び出します システムコール。コードとメモリがコピーである新しいプロセスを作成します。サブシェルが作成されるとき、サブシェルとその親の間にほとんど違いはありません。特に、それらは同じ変数を持っています。 $$
でさえ 特殊変数は、サブシェルで同じ値を保持します。これは、元のシェルのプロセスIDです。同様に$PPID
元のシェルの親のPIDです。
いくつかのシェルは、サブシェルのいくつかの変数を変更します。 BashはBASHPID
を設定します サブシェルで変更されるシェルプロセスのPIDに。 Bash、zsh、およびmkshは、$RANDOM
を手配します 親とサブシェルで異なる値を生成します。ただし、このような組み込みの特殊なケースを除いて、すべての変数は、サブシェルで元のシェルと同じ値、同じエクスポートステータス、同じ読み取り専用ステータスなどを持ちます。すべての関数定義、エイリアス定義、シェルオプション、および他の設定も継承されます。
(…)
によって作成されたサブシェル 作成者と同じファイル記述子があります。サブシェルを作成する他のいくつかの手段は、ユーザーコードを実行する前にいくつかのファイル記述子を変更します。たとえば、パイプの左側はサブシェル内を走り、標準出力がパイプに接続されています。サブシェルも同じ現在のディレクトリ、同じシグナルマスクなどで始まります。いくつかの例外の1つは、サブシェルがカスタムトラップを継承しないことです:無視されたシグナル(trap '' SIGNAL
)サブシェルでは無視されたままですが、他のトラップ(trap CODE
SIGNAL )デフォルトのアクションにリセットされます。
したがって、サブシェルはスクリプトの実行とは異なります。スクリプトは別のプログラムです。この別個のプログラムは、偶然にも親と同じインタプリタによって実行されるスクリプトである可能性がありますが、この偶然は、別個のプログラムに親の内部データに関する特別な可視性を与えるものではありません。エクスポートされない変数は内部データであるため、子シェルスクリプトのインタープリターが実行されると、これらの変数は表示されません。エクスポートされた変数、つまり環境変数は、実行されたプログラムに送信されます。
したがって:
x=1
(echo $x)
1
を出力します サブシェルは、それを生成したシェルの複製であるためです。
x=1
sh -c 'echo $x'
たまたまシェルの子プロセスとしてシェルを実行しますが、x
2行目はx
との接続がありません 2行目よりも
x=1
perl -le 'print $x'
または
x=1
python -c 'print x'
例外はksh93
です フォークが最適化され、その副作用のほとんどがエミュレートされるシェル。
意味的には、それらはコピーです。実装の観点からは、多くの共有が行われています。
右側は、シェルによって異なります。
これをテストする場合は、$(trap)
などに注意してください。 元のシェルのトラップを報告する場合があります。また、多くのシェルには、トラップを含むコーナーケースにバグがあることにも注意してください。たとえば、ninjaljは、bash 4.3の時点で、bash -x -c 'trap "echo ERR at $BASH_SUBSHELL $BASHPID" ERR; set -E; false; echo one subshell; (false); echo two subshells; ( (false) )'
ERR
を実行します 「2つのサブシェル」の場合はネストされたサブシェルからトラップしますが、ERR
はトラップしません 中間サブシェルからのトラップ— set -E
オプションはERR
を伝播する必要があります すべてのサブシェルにトラップしますが、中間のサブシェルは最適化されていないため、ERR
を実行するためにそこにありません トラップ。