web-dev-qa-db-ja.com

変数がサブシェルに表示されるのはなぜですか?

Learning Bash Bookでは、サブシェルは環境変数やファイル記述子などのみを継承し、エクスポートされない変数は継承しないと述べています。

_$ var=15
$ (echo $var)
15
$ ./file # this file include the same command echo $var

$
_

私が知っているように、シェルは_()_と_./file_の2つのサブシェルを作成しますが、なぜ_()_の場合、サブシェルはvar変数を識別しますが、エクスポートされません。そして_./file_の場合、それはそれを識別しませんでしたか?

_# Strace for () 
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25617
# Strace for ./file
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f24558b1a10) = 25631
_

私はstraceを使用してこれがどのように発生するかを理解しようとしましたが、意外なことに、bashはcloneシステムコールに同じ引数を使用することがわかりました。つまり、これは_()_と_./file_は親の同じプロセスアドレススペースを持つ必要があるため、_()_の場合はなぜサブシェルに変数が表示され、_./file_の場合は同じことが起こりませんが、同じ引数がクローンシステムコールに基づいていますか?

18
user3718463

Learning Bash Bookは間違っています。サブシェルはすべての変数を継承します。 $$(元のシェルのPID)も保持されます。その理由は、サブシェルの場合、シェルはforkし、新しいシェルを実行しないためです(逆に、./fileと入力すると、新しいコマンドが実行されます(例:新しいシェル)。strace出力で、 execveなどをご覧ください)。したがって、基本的に、これは単なるコピーです(いくつかの文書化された違いがあります)。

注:これはbashに固有のものではありません。これはどのシェルにも当てはまります。

15
vinc17

あなたか本のどちらかがサブシェルをシェルであるサブプロセスと混同しています。

一部のシェル構成では、シェル forking が子プロセスになります。 Linuxでは、forkは、より一般的なcloneシステムコールの特殊なケースであり、straceログで確認できます。子はシェルスクリプトの一部を実行します。子プロセスはサブシェルと呼ばれます。そのような最も直接的な構成は_command1 &_です。_command1_はサブシェルで実行され、後続のコマンドは親シェルで実行されます。サブシェルを作成する他の構成には、コマンド置換$(command2)およびパイプ_command3 | command4_(_command3_がサブシェルで実行され、_command4_がほとんどのシェルでサブシェルで実行されますがkshでは実行されません)が含まれますまたはzsh)。

サブシェルは親プロセスのコピーであるため、同じ環境変数だけでなく、内部定義もすべて同じです。変数(_$$_、元のシェルプロセスのプロセスIDを含む)、関数、エイリアス、オプションなど。サブシェルでコードを実行する前に、bashは変数BASHPIDを子プロセスのプロセスIDに設定します。

_./file_を実行すると、外部コマンドが実行されます。まず、シェルは子プロセスをフォークします。次に、この子プロセス executesexecveシステムコールを使用)実行可能ファイル_./file_。子プロセスは、その親のプロセス属性(環境、現在のディレクトリなど)を継承します。アプリケーションの内部的な側面はexecve呼び出しで失われます:エクスポートされない変数、関数などは、カーネルが行うbashの概念です知らない、そしてbashが別のプログラムを実行するとそれらは失われる。その他のプログラムがたまたまbashスクリプトであったとしても、その親プロセスが偶然bashのインスタンスであることを知らないか気にしないbashの新しいインスタンスによって実行されます。したがって、Shell変数(エクスポートされない変数)はexecveを存続しません。