括弧は本当にコマンドをサブシェルに入れますか?


94

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

x=1

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

echo $x期待どおり、スクリプトで実行しても何も起こりません

回答:


134

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

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

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

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

副<文>この[前述の事実の]結果として、それ故に、従って、だから◆【同】consequently; therefore <文>このような方法で、このようにして、こんなふうに、上に述べたように◆【同】in this manner <文>そのような程度まで<文> AひいてはB◆【用法】A and thus B <文>例えば◆【同】for example; as an example:

x=1
(echo $x)

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

x=1
sh -c 'echo $x'

シェルの子プロセスとしてシェルを実行するために起こりますが、x2行目は、とのこれ以上の接続を持っているxよりも、2行目を

x=1
perl -le 'print $x'

または

x=1
python -c 'print x'

1 例外は、ksh93フォークが最適化され、その副作用のほとんどがエミュレートされるシェルです。
2 意味的に、それらはコピーです。実装の観点から見ると、多くの共有が行われています。
3 右側については、シェルに依存します。
4 これをテストする場合、次のようなもの$(trap)が元のシェルのトラップを報告する可能性があることに注意してください。また、多くのシェルには、トラップが関係するコーナーケースにバグがあることに注意してください。たとえば、ninjaljは、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) )'実行しますが、中間サブシェルからのトラップではERRなく、ERRset -EERRすべてのサブシェルにトラップしますが、中間サブシェルは最適化されて離れているため、ERRトラップを実行するためにそこにはありません。


2
@クサラナンダ号(x=out; (x=in; echo $x)
ジル

2
@ flow2kこれは、同じレベルで発生するものの拡張の順序です。ただし、評価と拡張がどのように混在するかを考慮する必要もあります。展開がネストされた構造の評価を必要とする場合、内部構造が最初に評価されます。したがって、たとえば、を評価するecho $(x=2; echo $x)には、フラグメント$(x=2; echo $x)を展開する必要があります。これには、コマンドの評価が必要x=2; echo $xです。の拡張は$x、この評価中に、パーツを評価した後に発生しx=2ます。
ジル

2
@ flow2kパラメーターの展開とコマンドの置換の間に順序はありません。この文ではセミコロンを使用して展開手順を分離していますが、パラメーターの展開とコマンドの置換は同じセミコロン区切りの句に含まれていることに注意してください(はい、微妙です)。順序の問題は、部品の1つに、(x未設定で)echo $(echo foo >somefile)${x-$(cat somefile)}またはなどの、他の部品に影響を及ぼす副作用がある場合echo $(echo $x),${x=1}です。
ジル

1
@Gilles; 私は混乱しています。サブシェルがスクリプトの実行と異なる場合、次のように言われます:シェルスクリプトを実行すると、新しいプロセスであるサブシェルが起動します。?また、サブシェル環境は、シェル環境の複製として作成されます。したがって、。/ fileはサブシェル環境で実行されるため、変数の割り当てによって設定されるシェルパラメーターを継承する必要があります。
ハック

2
@haccks ABSの定義は近似値であり、あまり良い値ではありません。例は良いですが、そのページの最初の2行は非常に単純化されているため、間違っています。別のスクリプトからスクリプトを実行すると、サブシェルではない新しいプロセスが起動します。SUSでは、定義は正しい(ただし、必ずしも非常に理解しやすいとは限らない)。./fileサブシェルでは実行されません。参照してくださいunix.stackexchange.com/q/261638unix.stackexchange.com/a/157962
ジル・

15

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

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

kshのmanページは、これをbashのページよりも少し明確にします。

man ksh

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

man bash

(リスト)

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

コマンド実行環境

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


3
これと対照的なのWhen a simple command other than a builtin or shell function is to be executed, it is invoked in a separate execution environment that consists of the following.は、次の項目を含む:(· shell variables and functions marked for export, along with variables exported for the command, passed in the environment同じman bashセクションから)echo $x-script xがエクスポートされない場合に-scriptが何も印刷しない理由を説明します。
ヨハンE 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.