2014年12月11日木曜日

バッククォートとサブシェルと私

シェルスクリプトにおいて、バッククォートで括った文字列はサブシェルでコマンドとして実行され、バッククォート文字列はサブシェルの標準出力の内容で置き換えられる。実行するのはあくまでサブシェルなので、バッククォートの中でシェル変数やカレントディレクトリを変更しても、元の親プロセスのシェルは影響を受けない。

% foo=10
% echo `foo=100; echo $foo`
100
% echo $foo
10

そう思っていたら…

% echo $$
69964
% echo `echo $$`
69964
あれっ? サブシェルのPIDを表示するつもりが、親プロセスのPIDが表示されてしまった。そこで、試しにpsしてみたところ、
% ps
PID TTY TIME CMD
69964 ttys000 0:00.01 sh
% (ps)
PID TTY TIME CMD
69964 ttys000 0:00.01 sh
サブシェルの子プロセスはどこ?

ここで、もしかしたらpsしか実行しないサブシェルはforkせずにpsをexecしているかもしれないと思い、psに続けて適当なコマンドをサブシェルで実行してみたところ、

% (ps; echo $foo)
PID TTY TIME CMD
69964 ttys000 0:00.02 sh
71038 ttys000 0:00.00 sh
10
ようやくサブシェルが姿を現した。バッククォートを使っても
% echo `ps; echo $foo`
PID TTY TIME CMD 69964 ttys000 0:00.02 sh 71186 ttys000 0:00.00 sh 10
ちと見づらいけど、ちゃんとサブシェルのPIDが見える。バッククォートでサブシェルが起動されるという認識は、間違っていなかった模様。

ここで気付いた。親シェルのシェル変数fooがサブシェルに見えているぞ。事前にexportとかしていないので、本来子プロセスには親プロセスのfooは見えないはずだが、バッククォートや()で起動されるサブシェルには、なんらかの方法でシェル変数も引き継がれるのか。そうか、だから$$も親プロセスの値が単純コピーされて、子プロセスで$$を見ても親プロセスと同じだったのだろう。

0 件のコメント:

コメントを投稿