角かっこは実際にコマンドをサブシェルに入れますか?

角かっこは実際にコマンドをサブシェルに入れますか?

私が読んだことによれば、コマンドを括弧内に入れると、スクリプトを実行するのと同様にサブシェルで実行する必要があります。これが真であれば、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シグナル)基本動作にリセットする

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

したがって:

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

おすすめ記事