web-dev-qa-db-ja.com

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

私が読んだことから、かっこでコマンドを入力すると、スクリプトを実行するのと同様に、サブシェルでコマンドを実行する必要があります。これがtrueの場合、xがエクスポートされない場合、変数xはどのように表示されますか?

x=1

ランニング (echo $x)コマンドラインでの結果は1

ランニング echo $xスクリプト内で、期待どおりに何も起こりません。

107
Igorio

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

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

_(…)_によって作成されたサブシェルは、その作成者と同じファイル記述子を持っています。サブシェルを作成する他のいくつかの方法は、ユーザーコードを実行する前に一部のファイル記述子を変更します。たとえば、パイプの左側はサブシェルで実行されます 標準出力がパイプに接続されています。サブシェルも同じ現在のディレクトリ、同じシグナルマスクなどで開始します。いくつかの例外の1つは、サブシェルがカスタムトラップを継承しないことです。無視されたシグナル(_trap '' SIGNAL_)はサブシェルで無視されたままですが、他のトラップ(_trap CODE_[〜#〜] signal [〜#〜])はデフォルトのアクションにリセットされます4

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

したがって:

_x=1
(echo $x)
_

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

_x=1
sh -c 'echo $x'
_

シェルをシェルの子プロセスとして実行しているが、2行目のxは2行目のxとの接続が

_x=1
Perl -le 'print $x'
_

または

_x=1
python -c 'print x'
_

1例外は_ksh93_シェルで、フォークが最適化され、その副作用のほとんどがエミュレートされます。
2意味的には、それらはコピーです。実装の観点からは、多くの共有が行われています。
右側については、シェルに依存します。
4これをテストする場合、 $(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) )'は「2つのサブシェル」の場合、ネストされたサブシェルからERRトラップを実行しますが、中間サブシェルからのERRトラップは実行しないことに注意してください— _set -E_オプションは、ERRトラップをすべてのサブシェルに伝達する必要がありますが、中間サブシェルは最適化されているため、ERRトラップを実行するためにそこにはありません。

もちろん、すべてのドキュメントに記載されているように、かっこで囲まれたコマンドはサブシェルで実行されます。

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

Kshのmanページでは、これがbashのページより少しわかりやすくなっています。

man ksh

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

man bash

(list)

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

コマンド実行環境

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

15
Mikel