Lambdaカクテル

ソフトウェア開発者です.玉石混淆です.

bashの`export`の役割と`exec $SHELL -l`について補習する

シェルをいじったり環境を構築したりする時によく使うexport.なんとなく使っているが一体何者なのか?後学のためのメモ.

CAVEAT: ほとんどbashマニュアルの抄訳です.詳細はそちらを参考にしてください.

tl;dr

  • シェルからプログラムを起動すると,シェル起動時の環境変数が渡される.
  • シェルスクリプト内で変数を割り当てても,その行以外で呼び出されるプログラムに変数は継承されない.
  • exportすることで変数が継承されるようになる.
  • ~/.bashrc~/.bash_profileexportした変数がシェルでも有効なのは,これらのファイルは同じプロセスで実行されたからである.

read more

プログラムが起動すると「環境」と呼ばれる文字列の配列が渡される.これはname=valueの形式のリストである.

bashは環境を操作する方法をいくつか提供している.起動時にシェルは自身の環境を読み込み,見付かった名前のパラメータを作成し,自動的に子プロセスにexportするようにする.実行されたコマンドは環境を継承する.exportdeclare -xコマンドは,コマンドや関数 *1 を環境に追加したり,環境から削除したりできる.環境の値が変更されると,新たな値が環境に加わり,古い値に取って代わる.実行されるどのコマンドが継承する環境もシェルの初期環境に由来しているが,シェル内で変更したり,unsetexport -nにより削除したり,exportdeclare -xにより追加したりできる.

任意の単純なコマンドや関数は,(CC=clang makeのように)環境の割り当てを頭に付けて実行することで一時的に環境に割り当てを追加することができる.これらの割り当てはそのコマンドの中でのみ影響する.

-k オプションが有効なときは,コマンドに前置していない(つまり後置した)環境の割り当てもコマンドに渡される.(何に使うのだろう?)

$ perl -e 'print $ENV{hoge} . "\n"'

$ hoge=fuga perl -e 'print $ENV{hoge} . "\n"'
fuga
$ set -k; perl -e 'print $ENV{hoge} . "\n"' hoge=piyo; set +k
piyo

コマンドが実行されると,変数$_にコマンドのフルパスが代入され,これが環境に渡される.

$ perl -e 'print $ENV{_} . "\n"'
/usr/bin/perl
$

.bashrcたちがシェルに変数を引き継げる理由

.bashrc.bashrcexportした変数がその後で動いているシェルでも有効になる理由は,これらのファイルがどのように呼び出されているかを知ることで理解することができる.

まず,exportした環境は子プロセスにしか継承されないので,親プロセスに影響を与えることはできない.そして.bashrcbashを起動しているわけではない.

その一方で,環境設定として呼び出されたはずの.bashrc.bash_profileexportされた環境は有効なままシェルに引き継がれる.なぜなら,これらのファイルは普段われわれがシェルスクリプトを実行する時のように新たなシェルプロセスで実行されているのではなく,同一のプロセスで実行されているからである.その証拠に,たいていの.bash_profileには. ~/.bashrcという記述がある..組込みコマンドはsourceと同一である.sourceは,指定したファイルを現在の環境で実行し,最後のコマンドの終了コードを返す.この過程で新たなbashプロセスが起動することはない.そして,.bash_profilebashによって起動時に同一プロセスで実行される.つまり,これらは最初から同じプロセスで動作するように仕組まれているので,シェルも環境をそのまま使うことができるというわけである.

これらの設定ファイルは内容的にはシェルスクリプトと同一の記述であるためシェルスクリプトの動作と混同してしまい,誤解が生じてしまうのであろう.

ちなみによく環境の再読み込みで使われるexec $SHELL -lは,source ~/.bashrcと結果こそほぼ同じであるが動作原理は全く異なる. execは動作中のbashを,プロセスを生成せずに後続のコマンドで置換する組込みコマンドである.そして多くのbashユーザにとって$SHELL"/bin/bash"である.つまり展開されたコマンドはexec /bin/bash -lとなる.ここでbash-lパラメータは--loginと等価で,あたかもログインシェルであるかのように振舞い,通常のシェルからbashを起動したときよりも多くの設定ファイルを読み込むオプションである.具体的には/etc/profileなどが読み込まれる.結果として,同一プロセスでまっさらに新たなbashが起動し,生まれ変わるのだ.

まとめると,

  • source ~/.bashrc(. ~/.bashrc)は~/.bashrcを読み込む
  • exec $SHELL -l~/.bashrc以外にもいろいろ読み込む

読み込まれるファイルの詳細は別記事にまとめてあるので,あわせて参照してほしい.

windymelt.hatenablog.com

参考文献

いつも通りのマニュアル.この手の動作は下手な解説読むよりマニュアルを読んだほうがより深く理解できます.

tiswww.case.edu

*1:関数の場合はexport -fを使う