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
_の場合は同じことが起こりませんが、同じ引数がクローンシステムコールに基づいていますか?
Learning Bash Bookは間違っています。サブシェルはすべての変数を継承します。 $$
(元のシェルのPID)も保持されます。その理由は、サブシェルの場合、シェルはforkし、新しいシェルを実行しないためです(逆に、./file
と入力すると、新しいコマンドが実行されます(例:新しいシェル)。strace出力で、 execve
などをご覧ください)。したがって、基本的に、これは単なるコピーです(いくつかの文書化された違いがあります)。
注:これはbashに固有のものではありません。これはどのシェルにも当てはまります。
あなたか本のどちらかがサブシェルをシェルであるサブプロセスと混同しています。
一部のシェル構成では、シェル forking が子プロセスになります。 Linuxでは、fork
は、より一般的なclone
システムコールの特殊なケースであり、strace
ログで確認できます。子はシェルスクリプトの一部を実行します。子プロセスはサブシェルと呼ばれます。そのような最も直接的な構成は_command1 &
_です。_command1
_はサブシェルで実行され、後続のコマンドは親シェルで実行されます。サブシェルを作成する他の構成には、コマンド置換$(command2)
およびパイプ_command3 | command4
_(_command3
_がサブシェルで実行され、_command4
_がほとんどのシェルでサブシェルで実行されますがkshでは実行されません)が含まれますまたはzsh)。
サブシェルは親プロセスのコピーであるため、同じ環境変数だけでなく、内部定義もすべて同じです。変数(_$$
_、元のシェルプロセスのプロセスIDを含む)、関数、エイリアス、オプションなど。サブシェルでコードを実行する前に、bashは変数BASHPID
を子プロセスのプロセスIDに設定します。
_./file
_を実行すると、外部コマンドが実行されます。まず、シェルは子プロセスをフォークします。次に、この子プロセス executes (execve
システムコールを使用)実行可能ファイル_./file
_。子プロセスは、その親のプロセス属性(環境、現在のディレクトリなど)を継承します。アプリケーションの内部的な側面はexecve
呼び出しで失われます:エクスポートされない変数、関数などは、カーネルが行うbashの概念です知らない、そしてbashが別のプログラムを実行するとそれらは失われる。その他のプログラムがたまたまbashスクリプトであったとしても、その親プロセスが偶然bashのインスタンスであることを知らないか気にしないbashの新しいインスタンスによって実行されます。したがって、Shell変数(エクスポートされない変数)はexecve
を存続しません。