UNIX/Linux及びシェルに限らず、他OS環境でも環境変数は利用されますが、シェル(bash)には環境変数の他にシェル変数があり、その環境変数とシェル変数には組み込み環境変数と組み込みshell変数、更に変数と特殊変数があります。
環境変数、シェル変数と言えば、一般には組み込み環境変数と組み込みshell変数を指しますが、他方、これらはユーザー定義ができるので、組み込み変数とはユーザー定義変数ではない環境変数やshell変数を指します。
尚、組み込み変数は、ログアウト後、再ログインしても常に利用できますが、ユーザー定義の環境変数やshell変数でコマンドラインから設定した変数については、有効な設定ファイルで設定しない限り、ログイン中のみ有効です(再ログイン時には存在しません)。
scriptやshell関数において設定された環境変数やshell変数は、一般の変数同様、スコープ内であれば、実行都度設定されるので実質、なんら設定をしなくても当該script、shell関数実行時に有効になるものの、後述しますが、特にshell関数と関数内のshell変数のスコープについては注意が必要です。
ここではsh/bashにおける環境変数とシェル変数という視点からその特徴や差異、shell関数における変数の注意点、それぞれの変数の確認方法や登録・削除などの設定方法などを取り上げます。
環境変数の環境とは、システム環境のことを指し、システム環境変数とも言えるものでシステム内で共有可能なメモリ空間(名前空間)にある参照・編集可能な変数でshellやその種類に関係なく保持される変数であり、UNIX/Linuxにおいて、とかくshellとの区別はつきにくいですが、shellにはshell固有のshell変数もあり、両者に共通の変数もあるものの、基本的に環境変数とshell変数とは区別されますし、両者は似て非なる別の種類の変数です。
環境変数を確認する方法は、いくつかあります。
$ echo $変数名
個々の環境変数を確認する方法の1つとして、その変数名を知っていれば標準出力コマンド[ echo ]を利用する方法があります。
変数を参照する際には変数名の前にドルマーク$を付加します(登録や削除の際は付加しません)。
$ env
$ printenv
$ declare
また、設定されている環境変数の一覧は、それぞれオプションなしの[env]/[printenv][declare]コマンドで確認できます。
[declare]コマンドでは環境変数以外にshell変数の両方の一部情報と配列、shell関数が含まれるbashが保持する一覧が表示されます。
ちなみにオプション付きで多様な機能を持つsetというコマンドもあり、オプションなしの[set]で表示される一覧にある変数は「現在実行中のシェル」が保持している共有可能なシェル変数です。
環境変数は、必要に応じて登録・設定、変更、削除など編集する事ができますが、変数の編集は参照時と異なり、変数名の前にドルマーク$は付加しません。
export 変数名=値
環境変数の登録は、bashの場合、3通りの方法があり、代入と同時に登録するか、
変数名=値 ; export 変数名
UNIX/Linux及びshellの特性からセミコロンでコマンドを継続して一行で代入後に登録するか、
変数名=値
export 変数名
代入と登録2回に分けて登録します。
export PATH=$PATH : .
export PATH=~/bin : $PATH
尚、PATHのように1つの環境変数に複数の値を設定する場合、UNIX/Linuxでは通常そのセパレータには : コロン(Windowsはパスにコロンを含むので ; セミコロン)を使用し、既に値が設定されている場合、このように設定し、この例の1つめはPATHの最後にカレントディレクトリ(ドット . )を追加、2つめはPATHの先頭にホームディレクトリ(チルダ・チルドとスラッシュ ~/ 、$HOMEと同様)のサブディレクトリbinを追加しています。
登録においてexportコマンドを使用する(環境変数を登録する)場合には、[env]/[printenv][declare][set]コマンド何れの一覧にも追加されます。
ちなみに代入の場合、イコールの前後には空白があってはいけません。
変数名と値をともに削除する場合
export -n 環境変数名
変数名を残して値だけを消去する場合
export 環境変数名=
bashの場合、環境変数の削除にもexportコマンドを使用します。
尚、exportコマンドで削除されるのは、[env]/[printenv]に登録・表示される変数であり、仮にexportコマンドで登録し、[declare][set]コマンドにも登録、表示される同一の変数がある場合でも[declare][set]で表示される変数は削除されません。
sh/bashには、環境変数とはまた別にsh/bashというshell自身が持つ共有メモリ空間があり、そこに保持された共有可能な変数をshell変数と呼ぶことがあります。
shell変数の確認方法は、いくつかあります。
$ echo $FOO
事前にshell変数名を知っていればechoコマンドで確認することもできます。
$ set
$ declare
shell変数の一覧を取得するには、オプションなしのsetコマンド、declareコマンドを使用します。
但し、declareコマンドで表示される一覧は、setコマンドで表示される一覧以上の環境変数が含まれ、且つ登録されていればユーザー定義のshell関数とその定義も表示されます。
$ declare FOO=bar
shell変数には、組み込みshell変数があり、一部を除き、基本的に編集も可能ですが、それとは別にdeclareコマンドでユーザー定義のshell変数を設定登録することができます。
但し、ユーザー定義のshell変数は、環境変数同様、ログイン中のみ有効なので、例えばコマンドラインで設定したユーザー定義のshell変数は再ログイン時には、オプションなしのsetコマンドやdeclareコマンドで確認しても存在しません。
再ログイン時でも常に有効にしたい場合には、有効な設定ファイルで設定する等の対応が必要です。
また、scriptまたは関数内で設定登録したshell変数は、実行都度設定されるので、実行時、そのスコープ内では有効、更にshell関数内でdeclareコマンドを使って設定された変数は、そのスコープはlocalコマンド使用時同様、局所変数には、なるものの、関数はともかく、そのshell関数が実行できる状態なら変数は実質、再ログイン時でも利用できます。
変数名と値をともに削除する場合
unset シェル変数名
変数名を残して値だけを消去する場合
declare シェル変数名=
bashの場合、shell変数を削除する方法として、変数自体を削除するにはunsetコマンド、変数の設定された値だけを削除する為にはdeclareコマンドを使用します。
尚、unset、declareコマンドの削除、値編集結果は、[env]/[printenv][declare][set]コマンド何れの一覧に登録表示される変数にも反映されます。
環境変数とシェル変数における主な注意点には登録・編集・表示において設定が重複するか否かとスコープの2つがあります。
環境変数 | シェル変数 | |
---|---|---|
登録・編集 | export | declare |
削除 | export -n(unset) | unset |
表示 | env printenv | set declare |
まず、1点めの注意点として環境変数とシェル変数の登録・編集はexport/declare、削除はexport -n/unset、表示はenv・printenv/set・declareと、かなり込み入っていることです。
更にシェル変数を表示するsetとdeclareは共に環境変数を含み、それでいてdeclareにはset以上に環境変数が多く含まれる上、更にユーザー定義関数やユーザー定義配列も登録でき、組み込み配列等も表示されます。
この使い分けをわかりやすく説明するのが難しいのですが、まず結果からして「割りきって考える」という決め打ちをする以外にないと思います。
「割りきって考える」というのは、心の中で、または、仲間内で見解の相違がないように調整して、はたまた規約などの書面上で、exportする変数は全て環境変数(環境変数は必ずexportする)と決め、declareする変数は全てシェル変数(シェル変数は必ずdeclareする)と決めつけて扱うということで、言い換えると
exportでset/declareの一覧に追加される変数の事は、全く知らないフリをして無視する
環境変数の削除にunsetが使えるという事は、全く知らないフリをしてexport -nに統一する
シェル変数がunsetされるとenv/printenvに同名の変数がある場合も削除されるが、所詮シェル変数だったんだと割りきって初めから環境変数には存在しないものとして無視する
シェル変数がdeclareで値消去を含め「編集」されるとenv/printenvに同名の変数がある場合も同様に編集されるが、どうせシェル変数だもんなと割りきって初めから環境変数には存在しないものとして無視する
ということになるはず!?です。
が、しかし、declareで登録・編集したシェル変数と同名の変数をexportで登録・編集すると反映されてしまう(逆も同じな)ので
環境変数とシェル変数がかぶらないように気を付ける必要がある
という点も要注意です。
シェルはシェルから起動することもでき、例えばログインしたそのシェルであるログインシェルとサブシェルやサブシェルとサブシェル・・・という関係が成り立ち、環境変数SHLVLにその階層を示す整数値がセットされますが、その上で環境変数とシェル変数にも影響があります。
まず、起動元のシェルとそのサブシェルにおける環境変数のスコープ(可視範囲・有効範囲)については、
ログイン時点で既にセットされている環境変数及び親シェルでセットされた環境変数は子シェル(サブシェル)に引き継がれるが、サブシェルでセットされた環境変数は親シェルには引き継がれない。
同じくシェル変数のスコープについては、
ログイン時点で既にセットされているシェル変数はサブシェルに引き継がれるが、ログイン後登録したシェル変数はサブシェルには引き継がれないし、サブシェルで登録したシェル変数はそのプロセスIDを持つシェル固有の変数なので親シェルには引き継がれない。
次にシェル関数のスコープについては、
コマンドラインでワンライナーまたは、コマンドラインエディタで作成したシェル関数は、そのシェル内でのみ有効なので常にサブシェルを起動して実行してしまうスクリプトではシェル関数自体が認識されない為、共有されない。
↓
スクリプトをshの.ドットコマンド、bashのsourceコマンドでカレントシェルに読み込むようにすれば回避できる可能性はある。
次にスクリプト間における環境変数、シェル変数、シェル関数のスコープについては
スクリプトからスクリプトを呼ぶ場合、「スクリプトは常にサブシェルを起動してそのサブシェル(派生として起動したシェル)上で実行される」為、呼び出し元のスクリプトと呼び出されたスクリプトは違うシェル上に存在することから、そのスクリプト上で設定した環境変数、シェル変数やシェル関数は起動元のスクリプトには引き継がれない。
↓
スクリプトをshの.ドットコマンド、bashのsourceコマンドでカレントシェルに読み込むようにすれば回避できる可能性はある。
ここで更に注意が必要なのが
スクリプトと関数ファイルに定義されたシェル関数の関係において関数の(ソースファイルの)登録は[ . ]コマンド、または[ source ]コマンドによって「現在のシェル」にそのソースを取り込むが、スクリプトは常にサブシェルを起動してそのサブシェル上で実行される為、サブシェル上で実行中のスクリプト内から起動元のシェルで定義された関数を認識できない為、共有されない。
↓
スクリプトもshの.ドットコマンド、bashのsourceコマンドでカレントシェルに読み込むようにすれば回避できる可能性はある。
ということです。。。が、bashでは、こうした現象を回避する為にdeclareによるshell変数の共有メモリ上ではなく、exportによる環境変数の共有メモリ上に登録すべく
$ export -f 関数名
とするとシェル間で関数を共有(、[$ export -n 関数名]で共有をやめることが)できるようになっています。
ただ、前述の通り
サブシェルで登録された環境変数及びシェル変数は、元のシェルでは参照できず共有されない
ということで元のシェルからサブシェルへはexportすれば共有されますが、反対はいずれにしても共有されないということです。
一方でexportの結果共有された関数内の変数(シェル変数ではなく関数内で定義される通常の変数)は、グローバル変数となってしまう為、仮に関数内(起動元シェル)とスクリプト内(サブシェル)で変数名がバッティングした場合にロジックの中で同じ変数名に違う値が設定されてしまうような可能性があり、仮にそうなった場合、非常に見つけにくいバグの元になってしまう可能性があるのでbashでは、これを回避し、変数を局所化する為に
local 変数名
local 変数名=値
localコマンドによって、そのシェル内で有効な変数として関数内でローカル変数を定義、宣言することができるようになっています(また、declareで登録された変数もシェル関数内で宣言された場合には局所化されます)。
尚、ここでは例示に「変数名」、「関数名」などの文字列を使っていますが、UNIX/Linuxの慣例上、例示にはhoge,hogehoge,foo,barなどを利用するのが一般的です。