Linux(具体的にはUbuntu 13.04)環境の動作を理解しようとしながら、環境変数の設定がさまざまなコンテキストで使用または定義されるさまざまな状況を発見しました。たとえば、確認すると、次のようなlocale
結果が得られます。
$ locale
LANG=en_US.UTF-8
LANGUAGE=es_ES:es_HN:es_EC:en
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=es_ES.UTF-8
// more output
LC_CTYPE
ただし、たとえば using が見つかった場合、env | grep "LC_CTYPE"
出力は送信されません。通常、locale
13個のLC_*
変数が表示されますが、env
9個のみが表示されます。
$ locale | grep "LC_*" | wc -l
13
$ env | grep "LC_*" | wc -l
9
異なる「属性」を持つ他の変数はですPS1
。たとえば、
$ env | grep "PS1" # No output, but...
$ set | grep "PS1" | head -n 1
PS1=$'\\[\\033[1;33m\\][\\t][\\W]\342\230\233\\[\\033[0m\\] '
もちろん、PS1
現在の環境では、プロンプトがそれに応じて変更されることがわかるので、明確に定義された変数です。
他の方法より他のコンテキストの環境変数が渡されstrace
、プログラムを実行すると何が起こるかを確認できるプログラムです。サンプル:
$ strace -v ./a.out # a.out is a common Hello World, made in C.
execve("./a.out", ["./a.out"], ["LC_PAPER=es_ES.UTF-8", ...]) = 0
brk(0)
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
# etc, etc
write(1, "Hello World\n", 12Hello World
) = 12
exit_group(0) = ?
プログラムを実行するときにシェルが最初にすることは call であり、execve
プログラムを呼び出します。最初のパラメーターは呼び出されたプログラム、2番目のパラメーターは呼び出されたプログラムargv
のパラメーター、3番目のパラメーターは環境変数です。
たとえば、3番目の引数はPS1
or does not listedですLC_TYPE
。
通常、に送信される環境変数のリストに表示またはenv
表示される変数。一部の変数はまたはに表示されますが、他の変数は表示されません(および、しかしnull値があります)。最後に、他の変数には目に見える効果()がありますが、反映されているように定義されていません。set
execve
locale
env
set
LC_TYPE
LC_COLLATE
LC_MESSAGE
LC_ALL
env
PS1
set
ここで何が起こっているのでしょうか? (パラメータなし)、(明らかにロケール変数のみが考慮される)env
の違いは何ですか?set
locale
答え1
$PS1
たとえば、レポートがない理由を説明する主な問題は、レポートに由来することenv
です。env
非対話型環境。プロセスはポイントで実行されます。インタラクティブシェルですが、環境の設定方法には微妙さがあります。実際には、exec()
すべてのプロセスに対して設定されたデフォルトのCレベルの外部変数から継承されます(参考資料を参照man environ
)。例は次のとおりです。
#include <stdio.h>
extern char **environ;
int main (void) {
int i;
for (i = 0; environ[i] != NULL; i++) {
printf("%s\n", environ[i]);
}
return 0;
}
興味深いことに、それをコンパイルして実行すると、見つかったものが報告されたものと**environ
正確に一致しますenv
。
$ gcc test.c
$ ./a.out > aout.txt
$ env > env.txt
$ diff env.txt aout.txt
68c68
< _=/bin/env
---
> _=./a.out
唯一の違いは実行可能ファイルの名前です。それでは、どこ**environ
から来て、なぜ含まれていませんか$PS1
?
基本的な説明は、プロセスは常に他のプロセスの子プロセスとして作成され継承されますが、**environ
プロセスPS1
の一部ではないということです。起動すると、シェルは標準位置から変数を取得できます。これは、シェルが対話型かどうかによって異なります。呼ぶ存在するman bash
。これの1つの側面は次のとおりです。
PS1が設定されました[...]Bashがインタラクティブな場合、シェルスクリプトまたは起動ファイルがこの状態をテストできるようにします。
次に、/etc/bashrc
次の内容を確認してください。
# are we an interactive shell?
if [ "$PS1" ]; then
実際の(クール)プロンプトが設定されており、そのプロンプトまたは初期値は編集されてい$PS1
ませんexport
。初期値は対話型なので、呼び出し時にシェルによって生成された次のファイルをインポートしますが、次のコマンドを実行するPS1
と**environ
これを見ることができます。
#!/bin/sh
echo $PS1
なし -echo $PS1
対話型シェルにあるときに定義されていても同じです。これは**environ
、実行が親対話型シェルの実行と#!/bin/sh
同じですがPS1
。**environ
**environ
環境変数)。
の内容は**environ
にあります/proc/[PID]/environ
。現在の対話型シェルで内容を確認すると、その内容がないcat /proc/$BASHPID/environ
ことがわかります。PS1
それでは、物事はどのように「環境」に入りますか?
簡単な答えはシステムコールを通してです。たとえば、前の例Cプログラムに何かを入れると、次のようになります。
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **environ;
int main (void) {
int i;
if (putenv("MYFOO=whatbar?")) {
fprintf(stderr, "putenv() failed: %s\n", strerror(errno));
exit(1);
}
for (i = 0; environ[i] != NULL; i++) {
printf("%s\n", environ[i]);
}
return 0;
}
MYFOO=whatbar?
出力に表示されます(参照man putenv
)。シェルはfork()
ing(親プロセスのメモリスタックコピー)と呼び出しexecv()
(コピーされたものを渡す)を介してプロセスを生成し、環境変数を子プロセスに**environ
渡すメカニズムを見ることができます。export
例に入れると、fork()
これが本当であることがわかります(繰り返し)、分岐プロセスと潜在的な実行プロセスは、子プロセスが作成され**environ
祖先から継承される方法です。呼び出しは、プロセスイメージを置き換えますが、exec
に応じてシステムによって渡されます(注:前者の一部のバージョンではこれを参照しません)。man execv
man environ
**environ
/usr/bin/env
これはMYFOO=whatbar?
エクスポートによるリテラルフォークと実行ですputenv()
。
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
extern char **environ;
int main (void) {
pid_t pid;
if (putenv("MYFOO=whatbar?")) {
fprintf(stderr, "putenv() failed: %s\n", strerror(errno));
exit(1);
}
pid_t pid = fork();
if (!pid) execl("/usr/bin/env", "env", NULL);
return 0;
}
それでは、「環境」以外のものはどこにありますか?
特定のシェルインスタンスの個人データです。 Bashは引数なしで継承された環境コンテンツを表示しますset
。この出力にはソース機能も含まれています。
ただし、たとえば env LC_CTYPE を使用して grep "LC_CTYPE" を検索すると、出力は送信されません。通常、localeは13個のLC_ *変数を表示しますが、envは9個のみを表示します。
LC_
env
(ただ)では変数を全く得られませんでしたが、LANG
では13個の変数を得ましたlocale
。私はこれがlocale
エクスポートされたのではなく、呼び出しによって設定された変数だと思います。それから情報を得るという事実は、env
おそらくいくつかの構成の無邪気なバグを反映するでしょう。
答え2
シェルは2種類の変数を知っています。
シェル(およびサブシェル)にのみ知られている「内部」変数
execve
エクスポートされた変数は、表示される「公式」変数ですenv
。シェル組み込みにはエクスポートされたexport
変数が表示されます。
実行すると
export PS1
そして繰り返す
env | grep "PS1"
そして、あなたはそれを見る。変数は生成中にエクスポートでき(export foo=bar
代わりにfoo=bar
)、生成または変更時に自動的にエクスポートでき(後でエクスポートできます)、set -a
「var=foo; ...; export var
エクスポートできません」()になりますexport -n var
。
シェルが「実際の」サブシェルを生成する場合(a|b
などを介して)、混乱を避けるためにエクスポートされていない複数の変数を保持します。(a;b)
$(a)
答え3
このコマンドの出力は、locale
現在の環境の環境変数のリストではありません。これは、コマンドkey=value
でenv
使用されるのと同じ形式で表示されるプロセス(特定の環境変数の影響を部分的に受け取る)の有効なロケールを表示します。
ここでeglibcコマンドの実装のソースコードを見ることができますlocale
。
http://www.eglibc.org/cgi-bin/viewvc.cgi/branches/eglibc-2_19/libc/locale/programs/locale.c?view=markup