環境変数ってなに? ( Linux )

# はじめに
## この記事の趣旨
以前の[標準入力・標準出力ってなに?](/angel_p_57/items/03582181e9f7a69f8168)に引き続き、Linux/UNIX初心者だとひょっとすると掴みづらいかもしれない概念や基本事項について説明していく試みです。
今回は、**環境変数**の話です。
なお、Windowsにも同じ用語があり、おそらく概念的にも似ているとは思うのですが、あくまでLinux/UNIXでのお話です。

## 注意
よく見かける環境変数の説明としては、「PATHに追加するとそのディレクトリにあるコマンドを実行できます」とか「LANGで言語を設定します」と言った使い方の話が多くなると思いますが、この記事ではあまりそういった話はしません。
標準的な環境変数については、例えば[POSIX規格の環境変数の章](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html)等が参考になると思います(英語ですが)。

# 環境変数ってなに?
## 超概要
簡単に言うと環境変数とは、**プログラムの挙動を調整するためのパラメータの一種**であって、**プロセス毎に保持・管理するもの**です。
なので、Linux/UNIXを扱う上で環境変数の知識が必要となるのは**環境変数を通じてプログラムの挙動を調整する場面がそれなりにある**からに他なりません。

## プログラムの挙動の調整
さて、上で出てきた「プロセス」というのは、OS上でのプログラムの実行単位[^1]を指す用語です。
ご存じのこととは思いますが、Linux/UNIXにおいては、同じプログラムを何度も、時には同時に実行させることができ、その時々で得られる結果というのは状況により変わります[^2]。

その結果を変える要因は、[標準入力・標準出力ってなに?](/angel_p_57/items/03582181e9f7a69f8168)で説明した標準入出力もそうですし、プログラムによっては各種設定ファイルが関与するかも知れませんし、様々なものがあります。
しかし、OSとしてプログラム実行時に指定するパラメータは、プログラムファイル名を除いて**2種類しかありません**[^3]。その1つが**コマンドライン引数**、もう一つが**環境変数**です。

念のためですが、コマンドライン引数というのは、文字通り**プログラムを実行するために指定したコマンド文字列そのもの**であり、例えば`ls -l`というコマンドを実行した場合だと、`ls`と`-l`という2つの文字列に分解された配列データ[^4]になります。

## プロセス毎のパラメータ
プログラム実行時にコマンドライン引数、環境変数を指定するわけですから、当然のように**コマンドライン引数のみならず環境変数もプロセス毎の管理**となります。
※前者については特に違和感なく納得できるかと思いますが、後者についてはそうでない人もいるかもしれません。

もしかしたら、マルチスレッドプログラムのことが気になる人がいるかもしれません。環境変数はプロセス内で共通、つまり**スレッド間の共有データ**となります。

そしてこのプロセス毎の情報は、Linuxの場合procファイルシステムを通じて参照することができます。

例えば、次のように同じsleepプログラムが2プロセス(PID 63,64)動いている状況があったとします。

```shell-session:sleepコマンドの状況
$ ps -fC sleep
UID        PID  PPID  C STIME TTY          TIME CMD
angel       63     5  0 17:51 pts/0    00:00:00 sleep 300
angel       64     5  0 17:51 pts/0    00:00:00 sleep 600
```

この時、各プロセスのコマンドライン引数・**実行時に指定された**環境変数はそれぞれ`/proc/PID/cmdline`,`/proc/PID/environ`というファイルを読む[^5]ことによって知ることができます。
※わざわざ「実行時に指定された」と言っているのは、その後内容が変化したとしても、**このファイルには反映されない**[^6]ということを意図しています。

なお、ファイル内部はNUL文字(ASCIIコード0)で区切られているので、trコマンドで改行に変えると読み易くなります。( NUL文字が使われていることは、odコマンドで確認することができます )

```shell-session:procファイルシステムから情報を得る例
$ od -An -tc /proc/63/cmdline
   s   l   e   e   p  \0   3   0   0  \0
$ tr \\0 \\n < /proc/63/cmdline
sleep
300
$ tr \\0 \\n < /proc/64/cmdline
sleep
600
$ od -An -tc /proc/63/environ
   P   A   T   H   =   /   b   i   n  \0   H   O   G   E   =   h
   o   g   e  \0
$ tr \\0 \\n < /proc/63/environ
PATH=/bin
HOGE=hoge
$ tr \\0 \\n < /proc/64/environ
PATH=/bin
UGE=uge
```

このことから、PID 63のsleepプログラムは、`sleep`,`300`の2項目をコマンドライン文字列、`PATH=/bin`,`HOGE=hoge`の2項目を環境変数として、PID 64の方は、`sleep`,`600`と`PATH=/bin`,`UGE=uge`をもって実行されたことが分かります。

# コマンドライン引数と環境変数の違い
ここまで、プログラム実行時に指定するパラメータとして、コマンドライン引数と環境変数があることを説明しました。これらはいずれも、プログラムの挙動を調整するためのものです。が、ではなぜ2種類あるのでしょうか。
その理由は(UNIXのOSとしての設計の問題なので)はっきりとは分かりません。が、推測することはできます。両者の違いを見ていくことで迫っていきます。

## データ形式の違い
まずはデータ形式の違いです。`/proc/PID/cmdline`,`/proc/PID/environ`両ファイルを読んだ例を抜粋して再掲します。

```shell-session:procファイルシステムから情報を得る例より抜粋
$ tr \\0 \\n < /proc/63/cmdline
sleep
300
$ tr \\0 \\n < /proc/63/environ
PATH=/bin
HOGE=hoge
```

コマンドライン引数は、単なる複数の文字列ですが、環境変数は`名前=値`のペアの集合体[^7]となっていることが分かります。ここから想像がつくかも知れませんが、両者は次のような違いがあります。

* コマンドライン引数  
  1つ以上の文字列からなる**配列** (順番にも意味がある)
* 環境変数  
  キー(名前)に値(文字列)を対応させた**連想配列** ( 順不同、キーの重複は想定しない )

「連想配列」というのは、プログラミング言語によってはハッシュや辞書(ディクショナリ)と呼ばれているものです。つまり環境変数で`HOGE=hoge`というデータがある場合、`HOGE`というキーに`hoge`という値を対応付けていることを意味します。

なお、意外かもしれませんが、キーとして、NUL文字(ASCIIコード0)と`=`が使えない以外に、特に文字に制限はありません。とは言え、実際に操作する場合に問題が出る可能性もある[^8]ので、ASCII文字の英数・アンダースコアに限定した方が無難ではあります。( 慣習的に大文字が使われることが多いです )

ちなみに、環境変数の中に特定のキーが無いという状態と、キーはあるが値が空(長さゼロの文字列)という状態は別物です。( 別物であっても同じように扱うプログラムもあるかもしれません )
以下は、dateコマンドでタイムゾーンを示すキー`TZ`が無い状態と、キーはあるが値が空の状態で実行させた例です。前者はシステムデフォルトのタイムゾーンが、後者はUTCが使われるという違いが出ています。

```shell-session:TZ(タイムゾーン情報)によるdateの挙動の違い
$ date  # キーTZなし、システムのデフォルトがJSTの場合
2018年 12月  2日 日曜日 20:35:01 JST
$ TZ= date  # キーTZに空文字を指定、UTC扱いになる
2018年 12月  2日 日曜日 11:35:08 UTC
$ date -u # コマンドライン引数でUTCを指定
2018年 12月  2日 日曜日 11:35:12 UTC
```

ところで、上記のように、環境変数は複数のデータの集合体なのですが、「環境変数のキー`TZ`に紐づいた値が…」というような物言いをするのは面倒だし誰もやらないので、一般に「`TZ`環境変数(の値)」と、あたかも個々にデータがあるかのような言い方をします。`TZ`も「キー」ではなく「環境変数名」です。

## 用法の違い
続いては両者の用法の違いです。
先ほどの`TZ`環境変数と`date`の`-u`コマンドライン引数のように効果がかぶるものもありますし、全く同じ効果を持つ環境変数・コマンドライン引数の組み合わせすら珍しくありません。

つまり、両者はそういった「効果」によって使い分けているわけではないのです。両者の使い分けは、むしろ管理上の理由が大きいものとなっています。

### 情報の冗長性  
コマンドライン引数は配列データである一方で、環境変数は連想配列です。つまり、前者は無用な情報が入っても選り分けるのが面倒な構造である一方、後者は使われないキーを無視するだけで済みます。  

つまり、**コマンドライン引数は本当に必要な情報だけ指定する**用法である一方で、環境変数は**様々なプログラムに指定する情報をマージして最大公約数的に指定する**という冗長性を持った用法が可能です。

### 共通処理と個別処理の分離
ここで、再度`TZ`環境変数を振り返ります。

先ほどは`date`コマンドでの例をあげましたが、実は`ls`や`stat`のファイルのタイムスタンプ関連等、時刻情報が絡む様々なプログラムにも効果があります。

```shell-session:lsへのTZ環境変数の効果
$ ls -l test.txt  # システム標準(この場合はJST)でのタイムスタンプ出力
-rwx------ 1 angel angel 398 12月  2 20:52 test.txt
$ TZ= ls -l test.txt  # タイムスタンプ出力をUTCに
-rwx------ 1 angel angel 398 12月  2 11:52 test.txt
```

これは、タイムスタンプ(UNIX時刻[^9])から年月日・時日秒を割り出すライブラリ関数`localtime`が、`TZ`環境変数によって挙動を変えるためです。逆に言うと、`ls`等のプログラム自体がこの環境変数を意識してしているわけではありません。

ここでちょっと考えてみましょう。プログラムを作る時、その全部の機能をゼロから作ることは稀で、大部分の処理は何らかライブラリを利用するはずです。
それらライブラリの中で、プログラムの主目的からすると些細な機能で、しかし調整可能なものを**全てコマンドライン引数で調整しようとしたらどうなるでしょうか**。コマンドライン引数の解釈がそれだけで恐ろしく煩雑になりますし、ライブラリにちょっとした変更が入るだけでもおおごとになってしまいます。

なので、**プログラム個別の機能に関わる部分はコマンドライン引数で、ライブラリのような共通機能は環境変数で調整するという棲み分け**は理に適っていると言えます。

ライブラリでなくても、複数のプログラムの類似の機能を制御するために、共通の環境変数を使う例もあります。言語を何にするか、英語か日本語か…etc.を制御する`LANG`環境変数[^10]なんかは、その典型と言えるでしょう。

※もちろん、だからと言って、**プログラム個別の機能を環境変数で制御してはいけないということはありません**。そのような使い方もよくあります。

## 環境変数の取り扱いと役割
ということで、環境変数には以下の使い方ができることを説明しました。

* (プログラムによっては使われないかもしれない)情報も含め、各種情報をマージして最大公約数的に指定する使い方ができること
* 個々のライブラリ等の共通機能の挙動を調整する使い方ができること

加えて、標準ライブラリや各種プログラミング言語において、プログラム実行をつかさどるAPIは多くの場合、今のプロセスの環境変数を、起動するプログラムに引き継ぐように作られています。[^11]
※これはOSが関与する話ではなく、**あくまでライブラリ等の慣習の話**です

以上の性質から、例えば一旦`LANG`環境変数を`ja_JP.UTF-8`(日本語UTF-8)と指定すると、そこから起動されるプロセスは ( 環境変数を引き継ぐことで )、全て(プログラムが対応している限り)日本語出力を行うように振る舞いを変えます。
そのため、様々なプログラムであたかも同じパラメータを共有しているように振る舞うことから、**各種プログラムの実行環境を司るグローバルな設定情報であるかのように**使われることになります。

しかし、ここで注意しなくてはなりません。環境変数というのは、あくまで起点となるプロセスから、「デフォルトで引き継ぐ」という挙動によって情報が受け渡されているに過ぎません。
**OSが何か不思議な力で共通設定をばらまいている訳ではない**ので、例えばsshでログインした時、例えばcronでジョブが実行された時、例えばWebサーバからサーバサイドアプリが実行された時、起点となるプロセスによって環境変数の内容が全然違うものになっている可能性があります。

そのため、環境変数については、どのプログラムからどのように情報が引き継がれるか、その影響範囲を意識した方が良いでしょう。

# 環境変数の操作
ここまで、プログラム実行時に環境変数を渡す話ばかりでしたが、プログラム起動後も**自分自身で環境変数を操作(キーの追加、削除、キーに対応する値の変更)することができます**。

以下では、各状況における環境変数の操作について見ていきます。

## 誰が環境変数を管理するか
プログラム起動時には、OSから環境変数を受け取りますが、その後の操作は専用のC言語APIが管理を担当します。OSは関与しません。
※そのため、操作した内容は`/proc/PID/environ`ファイルに反映されません。

代表的なAPIは次の通りです。

* [getenv](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/getenv.3.html)  
  指定したキーに対応する値を取得する
* [putenv](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/putenv.3.html),[setenv](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/setenv.3.html)  
  指定したキーを追加する、あるいは指定したキーに紐づいた値を変更する
* [unsetenv](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/unsetenv.3.html)  
  指定したキーを削除する
* [clearenv](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/clearenv.3.html)  
  全てのキーを削除する 

なお、**環境変数の変更操作はスレッドセーフではありません**。
マルチスレッドプログラムにおいて、環境変数自体がスレッド間共有データですから、変更を行う時はスレッド間の整合性を考えないと動作異常の原因となり得ますし、異なるスレッドで別々の環境変数を使うようなことはできません。

## シェル等で環境変数を操作する
プログラム実行を制御するのがシェルの一番の役割ですから、自然とシェルで環境変数を操作するのが、一番代表的な方法になるかと思います。

そしてシェルの場合、**シェル変数の延長で環境変数が扱える**ようになっています。
※そのため、環境変数がシェル特有の機能だと思っている方もいるのではないでしょうか…?

### シェルでの環境変数の操作
具体的には、bashの場合、通常の変数の参照・変更と環境変数の参照・変更は全く同じ方法で行えます[^12]。ひとつ違う点は、「環境変数であるという属性をつけるかどうか」です。
ある変数が通常の変数か、環境変数かどうかは、`declare`コマンドで見ることができます。この`declare`コマンドは環境変数属性を付与するためにも使えます。( 一般的なのは`export`コマンドですが )

```shell-session:変数の操作例
$ declare -p SHELL  # SHELLは通常は環境変数 ( -x で分かる )
declare -x SHELL="bash"
$ echo $SHELL  # $変数名 で参照可能
bash
$ HOGE=hoge  # 単に変数を定義するとデフォルトでは通常の変数
$ declare -p HOGE  # -x がないので通常の変数と分かる
declare -- HOGE="hoge"
$ echo $HOGE  # $変数名 で参照可能
hoge
$ declare -x HOGE  # 環境変数属性の付与 ( 属性を消す場合は +x で行う )
$ declare -p HOGE  # 環境変数に変わったことが分かる
declare -x HOGE="hoge"
$ HOGE=hogehoge    # 一旦環境変数属性がつけば、値を変えても属性はそのまま
$ declare -p HOGE  # 依然として環境変数
declare -x HOGE="hogehoge"
$ UGE=uge; export UGE  # export も環境変数属性を行う ( 定義もまとめて1コマンドでできる )
$ declare -p UGE  # 環境変数になっていることが分かる
declare -x UGE="uge"
$ unset UGE  # 通常の変数も環境変数もどちらも削除
$ declare -p UGE  # 削除されているのでエラーになる
bash: declare: UGE:  見つかりません
```

このように設定した環境変数は、シェル自体の挙動にも影響しますし、シェルから実行するコマンド全般にも影響します。

なお、環境変数の全ての情報を見る場合、変数名を指定せずに`declare -x`コマンドを実行します。或いは、`printenv`コマンドを実行しても良いでしょう。

### 一時的な環境変数の設定
ただし、コマンド実行時に一時的に環境変数を設定することや、或いは環境変数を削除した状態で ( 引き継がせないように ) コマンドを実行することもできます。

```shell-session:一時的な変数の変更
$ declare -p LANG TZ   # LANGは日本語UTF、TZはなし(デフォルトのタイムゾーン)
declare -x LANG="ja_JP.UTF-8"
bash: declare: TZ:  見つかりません
$ date -d 2018-12-25T00:00+0900
2018年 12月 25日 火曜日 00:00:00 JST
$ LANG= TZ=America/Los_Angeles date -d 2018-12-25T00:00+0900 # 一時的にLANG,TZを設定
Mon Dec 24 07:00:00 PST 2018
$ declare -p LANG TZ   # シェル自身の環境変数は変化していない
declare -x LANG="ja_JP.UTF-8"
bash: declare: TZ:  見つかりません
$ ( exec -c date -d 2018-12-25T00:00+0900 )  # 環境変数がクリアされた状態で実行、日本語出力ではなくなる
Tue Dec 25 00:00:00 JST 2018
$
```

なお、上の例、`exec`コマンドを丸カッコで挟んでサブシェルで実行しているのは、`exec`コマンドが**今のシェルを置き換える形でプログラムを実行する**効果を持つからです。これ以上シェルの実行を継続させる必要がなければ、丸カッコはなくても構いません。
~~或いは、丸カッコもコマンド名なしで単に`exec -c`を実行した場合、実行中のシェルの環境変数がクリアされます。~~※すみませんこの記述は勘違いだったようなので取り消します

ただ、bashの場合環境変数の一時的な設定と一時的なクリアを併用することはできません。併用する場合は、`env`コマンドを使う方法があります。

```shell-session:envコマンドの例
$ date -d 2018-12-25T00:00+0900  # 素の状態
2018年 12月  2日 日曜日 20:41:02 JST
$ env TZ=America/Los_Angeles date  # TZのみ設定
2018年 12月  2日 日曜日 03:41:12 PST
$ env - /bin/date  # 環境変数をクリアして実行
Sun Dec  2 20:41:30 JST 2018
$ env - PATH=/bin:/usr/bin TZ=America/Los_Angeles date  # 環境変数をクリアした上でTZを設定
Sun Dec  2 03:41:44 PST 2018
```

このように、単に環境変数を一時的に設定することも、クリアだけすることも、両方を併用することもできます。
ただし、環境変数をクリアした場合、`PATH`環境変数もクリアされてしまいますから、コマンド名からプログラムファイルを自動で検索してくれなくなります。なので、`/bin/date`のようにファイル名を明示するか、`PATH`環境変数を改めて設定して対応する必要があります。

### 他のツールの例
もう1つの例として、ツール内で管理している変数と環境変数とが近い扱いになる`make`を紹介します。

`make`は伝統的に、プログラムのビルド ( 複数ファイルに分かれているプログラムのコンパイルとリンク、システムディレクトリへのインストール ) を行うために使われてきたツールです。

以下は、`make`の挙動を制御するファイル`Makefile`を作って、それを通じて変数の内容と環境変数の内容を出力する例です。使い方を説明する余裕はとてもありませんが、扱いが近いものになっている様を見て頂ければ、と思います。

```shell-session:make自身で変数を使う例
$ cat > Makefile   # Makefileを先に作っておく、Ctrl-Dで入力終了
HOGE ?= hoge # 変数の設定 ( 未設定の時のみ )、$(変数名) で参照
default:
# 変数の内容を出力
        @echo $(HOGE)
# 環境変数を出力 ( 設定されていれば )
        @printenv HOGE
^D
$ make  # make内の変数は環境変数ではないのでprintenvに失敗する
hoge
Makefile:4: ターゲット 'default' のレシピで失敗しました
make: *** [default] エラー 1
$ HOGE=hogehoge make  # 環境変数として設定されていても変数のように扱われる
hogehoge
hogehoge
$ make HOGE=hogehoge  # コマンドライン引数に指定すると環境変数として設定される
hogehoge
hogehoge
```

## スクリプト言語で環境変数を操作する
次に、各種スクリプト言語(Perl,Python,Ruby)で環境変数を操作する方法についても少し触れます。

これらの言語では、あたかも連想配列(辞書/ハッシュ)を操作するかのように、環境変数を操作することができます。
Perlであれば`%ENV`、Pythonであればosモジュールの`os.environ`、Rubyであれば`ENV`が該当します。

これらは単なる変数ではなく、操作を通じて`getenv`や`putenv`と言ったC言語APIを実行する、特殊なデータになっています。

以下、`TZ`環境変数を設定して現在時刻の表示に反映させるスクリプトをそれぞれ示します。

```date.pl
use POSIX;

print strftime("%c",localtime),"\n";
$ENV{TZ}="America/Los_Angeles";
print strftime("%c",localtime),"\n";
```

```date.py
import datetime
import os

print(datetime.datetime.now().strftime("%c"))
os.environ["TZ"]="America/Los_Angeles"
print(datetime.datetime.now().strftime("%c"))
```

```date.rb
puts Time.now.strftime("%c")
ENV["TZ"]="America/Los_Angeles"
puts Time.now.strftime("%c")
```

これらは、同じ`strftime`というAPIで同じ出力形式を指定しています。それぞれ環境変数の変更前後で、タイムゾーン分の時刻のずれが出ていることが分かります。

```shell-session:各スクリプトの実行
$ unset LANG
$ perl date.pl
Sun Dec  2 18:47:35 2018
Sun Dec  2 01:47:35 2018
$ python3 date.py
Sun Dec  2 18:47:40 2018
Sun Dec  2 01:47:40 2018
$ ruby date.rb
Sun Dec  2 18:47:44 2018
Sun Dec  2 01:47:44 2018
```

## 環境変数操作の注意点
環境変数をプログラム実行中に操作するにあたり、注意点が1つあります。

それは、**環境変数を変えたとしてもその影響が直ぐに現れるとは限らない**ということです。
なぜならば、ライブラリによってはプログラムの起動時にのみ環境変数を見て初期化処理を行い、以降は環境変数の変化を追わない場合があるからです。

実は、言語を調整する`LANG`環境変数もそのような面を持っています。
次のPerlの例のように、環境変数変更後、`POSIX::setlocale`を明示的に呼ばないと、`変更が反映されません[^13]。

```shell_session:PerlでのLANGの反映
$ # 最初からLANGが設定されていれば日本語で出力される
$ LANG=ja_JP.UTF-8 perl -CO -MPOSIX -E 'say strftime("%c",localtime)'
2018年12月02日 19時55分54秒
$ # 起動後にLANGを変えるだけでは日本語にならない
$ LANG=C perl -CO -MPOSIX -E '$ENV{LANG}="ja_JP.UTF-8"; say strftime("%c",localtime)'
Tue Dec  2 19:56:21 2018
$ # setlocaleによってLANGの変化が反映される
$ LANG=C perl -CO -MPOSIX -E '$ENV{LANG}="ja_JP.UTF-8"; setlocale(LC_ALL,""); say strftime("%c",localtime)'
2018年12月02日 19時56分34秒
```

実行する他のプログラムへ引き継ぐための環境変数であれば、さほど意識する必要はありませんが、自プロセスの挙動を変えたい場合、その環境変数の変化がいつライブラリなりに反映されるのか、注意した方が良いと思います。

# おわりに
## まとめ
ということでまとめです。

* 環境変数とは
    * プログラムの挙動を調整するためのパラメータの1種である
    * プロセス毎に保持・管理する
    * マルチスレッドプログラムにおいてはスレッド間共有データとなる
* コマンドライン引数との違いは
    * コマンドライン引数・環境変数ともに、プログラム起動時に指定するパラメータである
    * コマンドライン引数は文字列の配列、環境変数は文字列の連想配列に相当する
    * コマンドライン引数はプログラム固有のパラメータであり、基本的に無駄な情報を含めない。一方で環境変数はプログラム自身からというよりも、各種ライブラリ・共通の機能からそれぞれ参照される性質の強いパラメータであり、使わない情報が含まれても良い(無視される)
* 環境変数の取り扱い
    * コマンドライン引数と同様、環境変数についても、OSは実行時にプログラムに情報を引き渡す部分しか関与しない
    * しかしプログラム実行を司るライブラリは、今のプロセスが保持する環境変数を、実行するプログラムへ引き継ぐ挙動をデフォルトで示す
    * 結果的に、様々なプログラムであたかも同じパラメータを共有しているように振る舞うため、各種プログラムの実行環境を司る設定情報として使われる
    * ただし起点となるプログラムが何であるかによって、環境変数の内容も大きく変わり得るため、環境変数がどこからどこに引き継がれるか、影響範囲を意識した方が良い
* 環境変数の操作
    * プログラム起動後も、各プロセス自身で環境変数を追加・変更・削除することができる
    * 環境変数の操作はライブラリの機能として、C言語APIを通じて行える (OSは関与しない)
    * シェルやmake等のツールでは、ツール自体の管理する変数の延長として環境変数が扱える
    * Perl,Python,Rubyといったスクリプト言語では、環境変数は一種の連想配列(ハッシュ/辞書)として操作できるようになっている

[^1]: **プロセス**: psやトップコマンドで一覧を出すことができますが、PIDというIDで区別されてますよね。アレのことです。
[^2]: **状況により変わる**: `/bin/true`コマンドのように、システム的なエラーがない限り常に同じ結果になるものもありますが、そういうコマンドばかりじゃ役に立ちませんよね
[^3]: **プログラム実行時に指定するパラメータ**: 詳細は、[exeve(2)のmanページ](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/execve.2.html)にあります。execveというのはプログラム実行を行うOSのAPI(システムコール)です。
[^4]: **分解された配列データ**: もちろん、シェルなんかではユーザは1つの文字列としてコマンドを入力するのですが、それが分解されて配列情報としてOSに渡されているのです。
[^5]: **ファイルを読む**: コマンドライン引数`/proc/PID/cmdline`は誰でも読むことができますが、環境変数`/proc/PID/environ`は、プロセス実行者或いはrootでないと読むことはできません。
[^6]: **反映されない**: 実を言うと、内容を後から変更する手段もないわけではないのですが、それについては割愛します。
[^7]: **名前=値のペア**: 名前=値でない形式のデータを渡すことは可能ではあります ( OSはただただ指定されたデータを渡すだけ )。ただそれがどういった影響を及ぼすかは不明ですし、おそらく何の保証もえられません。
[^8]: **問題が出る可能性**: 簡単に言うと、そういった環境変数はシェルで操作できなくなります。
[^9]: **UNIX時刻**: UNIX時刻は、Epochと呼ばれる1970/1/1 00:00からの経過秒数で管理している時刻のことです。
[^10]: **LANG環境変数**: そうは言っても、LANG環境変数はライブラリ関数`setlocale`による言語設定に影響を与えるという面が重要なので、どちらかというとライブラリ用なのかもしれませんが。
[^11]: **実行を司る標準ライブラリ**: いわゆる、execファミリーと呼ばれるライブラリ関数群が該当します。詳細は[exec(3)のmanページ](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/exec.3.html)を参照してください。もちろん、環境変数を指定できるAPI(`execle`,`execvpe`)もありますが、それ以外のAPIは環境変数を引き継ぐ挙動を示します。
[^12]: **通常の変数の延長**: csh/tcshの場合は設定・変更用のインターフェースは分かれています ( 環境変数用は `setenv`,`unsetenv`コマンド )。しかし、`$変数名`で内容を参照できるのはbashと同様です。
[^13]: **setlocaleを呼ばないと反映されない**: 逆に言えば、プログラム起動時のみ`setlocale`を呼んで言語を自動設定している、とも言えます。ただし`LANG`環境変数が影響する中で`setlocale`と関係ない処理であれば、即座に影響が出るかもしれません。なお、他のプログラムに環境変数を引き継ぐことを考えないのなら、`LANG`環境変数を設定し直さずに`setlocale`で直接言語を指定した方が話が早いです。