bashのちょっとしたバグと環境変数の話

変数の処理に関わるbashのバグっぽい挙動と、そこから推測される「環境変数」の扱いについての考察をまとめたもの。
7
angel (as ㌵㌤の猫) @angel_p_57

シェル自身が変数を管理しているし、処理もシェル内部で完結しているわけだから、わざわざ環境変数にする意味がないのである。処理のたびに環境変数のチェック→シェル変数のチェックとする意味がない。シェル変数だけを見れば済むのだから。

2021-02-23 19:07:35
angel (as ㌵㌤の猫) @angel_p_57

起動時にbash自身に引き継がれた環境変数は、というと、起動時に「エクスポート属性のついたシェル変数」として取り込んでいる。それで一元管理ができている。

2021-02-23 19:07:47
angel (as ㌵㌤の猫) @angel_p_57

なお、外部コマンド実行時には「bash自身の環境変数」を引き渡すわけだけども、その時にシェル変数の状況を都度反映してあげれば良い。つまり環境変数は情報のコピーとしてしか意味を持たず、二重管理にはなっていない。

2021-02-23 19:08:01
angel (as ㌵㌤の猫) @angel_p_57

では2つ目のデメリット、それは「環境変数を変えるだけで済まない問題がある」という点だ。

2021-02-23 19:08:13
angel (as ㌵㌤の猫) @angel_p_57

逆に言えば、環境変数を設定すればそれで済む問題なら、喜んで環境変数で管理すれば良かったのに(仮定法)、ということである。

2021-02-23 19:08:27
angel (as ㌵㌤の猫) @angel_p_57

おそらく、その筆頭は、言語設定を司る LANG, LC_XX系の環境変数だと思われる。

2021-02-23 19:08:37
angel (as ㌵㌤の猫) @angel_p_57

なぜかというと、環境変数の一部 or 大部分は、「プログラム起動時に、引き渡されてきた値に応じて設定を調整する」という扱いがされている。環境変数を「常に変化するもの」と見て、処理のたびに追うのは面倒だからだろう。

2021-02-23 19:08:48
angel (as ㌵㌤の猫) @angel_p_57

LANG, LC_XX系もその扱いになっていて、プログラム起動時の初期化処理の中で setlocale というAPIを呼ぶときに参照される。なので、途中でただ値を変更するだけでは意味がない。再度同じAPIを呼ぶ必要がある。

2021-02-23 19:08:59
angel (as ㌵㌤の猫) @angel_p_57

しかも、setlocale を呼ぶ時には、敢えて環境変数を経由させる意味がない。APIに直接値を渡すことも可能だからだ。…ってことを考えると、環境変数変更→setlocale なんて、とても面倒なことになる。

2021-02-23 19:09:13
angel (as ㌵㌤の猫) @angel_p_57

そういう観点で bash の挙動を追ってみると、export LANG=xxx 等で値を変更した場合、然るべきタイミングで setlocale を呼び直すだけで、環境変数への反映には頼ってないことが分かる。( 外部コマンド実行時には反映してるけど )

2021-02-23 19:09:28
angel (as ㌵㌤の猫) @angel_p_57

他は調べてないけど、おそらく必要に応じて、「変数の変化」に必要なフックを管理するようにしてるのではないだろうか。

2021-02-23 19:09:40
angel (as ㌵㌤の猫) @angel_p_57

ということで、この2つが環境変数に頼ってない理由かな、と考察できたのが面白かった。

2021-02-23 19:09:51
angel (as ㌵㌤の猫) @angel_p_57

…ただ、後から気付いたけど、環境変数に頼らないといけない場面もありそう。典型的なのは時刻に関わる TZ (タイムゾーン) 環境変数。

2021-02-23 19:10:01
angel (as ㌵㌤の猫) @angel_p_57

これは実を言うと、時刻関連の処理のたびに変化してないかチェックされる、LANG等とは真逆の性格の環境変数で、しかも API の tzset で陽に設定を行うときも環境変数を経由しなければならない。

2021-02-23 19:10:12
angel (as ㌵㌤の猫) @angel_p_57

環境変数を経由する必要のない setlocale とは対照的である。

2021-02-23 19:10:23
angel (as ㌵㌤の猫) @angel_p_57

で、bash に時刻処理なんてあったっけ? と考えてみると、コマンド履歴(history)記録時の時刻だとか、printf での T 書式だとかがあった。果たして、bash の挙動を追ってみたところ、TZ だけは即座に環境変数に反映するようだ。あらら。

2021-02-23 19:10:35
angel (as ㌵㌤の猫) @angel_p_57

なお、ソース追ってないのにどうやって環境変数の変化なんか分かるんじゃい、と疑問に思う人いるかも。

2021-02-23 19:10:49
angel (as ㌵㌤の猫) @angel_p_57

実はこれは簡単。デバッガの gdb で bash のプロセスを attach すれば良い。"p __environ[数字]" で、持っている環境変数を見ることができる。デバッグシンボルがないバイナリでも問題ない。後は適当にブレークポイントを考えるだけの話。

2021-02-23 19:11:02
angel (as ㌵㌤の猫) @angel_p_57

Linuxの /proc/プロセスID/environ ファイルのことを知ってる人もいるかも知れないけど、あっちは「起動時に引き渡された環境変数」なので、見ても環境変数の変化を追うことはできないので要注意。

2021-02-23 19:11:15
angel (as ㌵㌤の猫) @angel_p_57

昔記事を書いたとき、environファイルのことは言ってたけど、gdb の方法は言ってなかった。これは単純に初心者向けではないから。 qiita.com/angel_p_57/ite…

2021-02-23 19:12:19
angel (as ㌵㌤の猫) @angel_p_57

取り敢えずこんなところかな。

2021-02-23 19:12:33