bashのちょっとしたバグと環境変数の話
- angel_p_57
- 2044
- 7
- 0
- 0
昨日調べたこの話題、実用性があるわけではないけど、シェルの動きを考える上ではなかなか興味深いものだった。まあ、記事にする程でもないので、連ツイで。 twitter.com/sukesan1984/st…
2021-02-23 18:57:14Quoraで質問して回答をいただき納得した気がする。 Kohei Yoshitoimiさんによる「bashで`HOME=/ cd`とすると"/"に移動し、`HOME=/ export`とすると環境変数HOMEは"/"に書き換わっていないのはなぜなのでしょうか?」への回答 jp.quora.com/bash%E3%81%A7H…
2021-02-22 11:39:51まず、このbashの挙動自体は単純にバグで良いと思う。実用上何か困るってことはないけど、挙動が一貫していないという意味で。
2021-02-23 18:57:51一応挙動を簡単に言うと、"KEY=val declare -xp" のように変数を指定した上で変数一覧を見るような「内部」コマンドを実行しても、この変数が反映されていないということになる。
2021-02-23 18:58:23先に前提として、"KEY=val command" の形式で「外部」コマンドを実行した場合、起動するプログラムに渡される環境変数として、この KEY の分も追加/修正される。それを念頭に置いて。
2021-02-23 18:58:44同じようにと言うか、内部コマンドであっても、例えば "IFS=○ read var" だと IFS への値の設定が read の挙動に影響を与える。
2021-02-23 18:59:09そうすると、"KEY=val declare -xp" という変数の一覧を出力するコマンドでは "declare -x KEY="val"" という出力が含まれることを自然と期待する。しかしそうではないという挙動になっている。
2021-02-23 19:00:02ところがちょっとコマンド替えると出力に含まれるようになる。例えば "KEY=val declare -xp KEY" がその一つ。これは変数一覧ではなく単体で出力するコマンドだ。
2021-02-23 19:00:19もう一つ、同じ一覧出力でもシェル関数を経由すれば含まれるようになる。例えば "mydec () { declare "$@"; }; KEY=val mydec -xp" がそうだ。
2021-02-23 19:00:42「挙動が一貫していない」というのはこのことを指す。そういう意味でバグと言ってよいだろう。影響範囲としては、declare と export、それから set あたりになるように思う。
2021-02-23 19:00:58ソースまで読んだわけではないので推測だけど、"KEY=val 内部コマンド or 関数" の形式の場合、外部コマンドの場合と違って、新しいプロセスを生成せず今のシェルの中で処理を進める、そこに鍵がある。
2021-02-23 19:01:18つまり、新しいプロセスに変数の変更を全て押し付けてシェル自身はそのままの状態を保てる外部コマンド実行と違い、変数の状況を一時的に変更して後で復帰する必要がある。そこの処理で出た問題ということになる。
2021-02-23 19:01:44"KEY=val declare -xp" と "KEY=val declare -xp KEY" で、違いが出るということは、「単一の変数参照」は自然な挙動だけども、「変数一覧の取得」に問題があると推定できる。
2021-02-23 19:02:05ここまでがバグの話。ただ、これ妙に思う人もいるかも知れない。「環境変数を設定してから内部コマンドの処理を実行して、終わってから復帰させるだけなのに、なんでバグ出してるの?」と。
2021-02-23 19:02:38よく、「"export KEY=val" とすると、KEYという環境変数が設定されますよ」という説明をする。それが間違っているわけではないんだけど、内部的にはそう単純ではない、ということ。
2021-02-23 19:03:22おさらいとして「環境変数とは何か」というと、標準ライブラリ libc で管理している、シンボル environ を通じて管理されている文字列データの集合体であって、getenv や setenv といったC言語APIで操作できるものだ。
2021-02-23 19:03:51「いやでも、Pythonだと os.environ、Rubyだと ENV というオブジェクトで操作できるのでは」と思うかも知れないけど、これは裏でそういったC言語APIを呼んで辻褄を合わせているから。
2021-02-23 19:04:10ところが、bash で "export KEY=val" を実行しても、そういう意味でのbashプロセス自身の環境変数には反映されない。いや、後で反映されるタイミングがあるんだけど、少なくとも即時ではない。
2021-02-23 19:04:38では、この時の KEY ってなんだと言われると、「エクスポート属性のついたシェル変数 KEY」ということになる。エクスポート属性は、外部コマンド実行時に環境変数として渡されるという効果を表すものだ。
2021-02-23 19:04:53つまり「exportは環境変数を設定しますよ」というのは、実行される外部コマンドの立場ではその通りだけど、bash自身としてはあくまで「環境変数として引き渡す予定のデータ」でしかない、ということで微妙に違うことになる。
2021-02-23 19:05:18尤も、bash が外部コマンドを実行する時は、どうやら「自分の環境変数を直接渡す」という方法を採っているようなので、結局「bash自身の環境変数」にもなるようだが。「後で反映されるタイミングがある」と言ったのはそういうこと。
2021-02-23 19:05:41じゃあ、なんでそんな違いを設けているのか? ということになるけど、おそらく「環境変数として管理するメリットが無いから」なんだろうと思う。ここが、今回の問題を考察してみて気付いた点だ。
2021-02-23 19:06:11例えば、内部コマンド cd は HOME変数の、read は IFS変数の影響を受ける。「環境変数」ではなく「シェル変数」になっている。
2021-02-23 19:06:59