それでは、物事はどのように「環境」に入りますか?

それでは、物事はどのように「環境」に入りますか?

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"出力は送信されません。通常、locale13個のLC_*変数が表示されますが、env9個のみが表示されます。

$ 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番目の引数はPS1or does not listedですLC_TYPE

通常、に送信される環境変数のリストに表示またはenv表示される変数。一部の変数はまたはに表示されますが、他の変数は表示されません(および、しかしnull値があります)。最後に、他の変数には目に見える効果()がありますが、反映されているように定義されていません。setexecvelocaleenvsetLC_TYPELC_COLLATELC_MESSAGELC_ALLenvPS1set

ここで何が起こっているのでしょうか? (パラメータなし)、(明らかにロケール変数のみが考慮される)envの違いは何ですか?setlocale

答え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 execvman 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種類の変数を知っています。

  1. シェル(およびサブシェル)にのみ知られている「内部」変数

  2. execveエクスポートされた変数は、表示される「公式」変数ですenv。シェル組み込みにはエクスポートされたexport変数が表示されます。

実行すると

export PS1

そして繰り返す

env | grep "PS1"

そして、あなたはそれを見る。変数は生成中にエクスポートでき(export foo=bar代わりにfoo=bar)、生成または変更時に自動的にエクスポートでき(後でエクスポートできます)、set -avar=foo; ...; export varエクスポートできません」()になりますexport -n var

シェルが「実際の」サブシェルを生成する場合(a|bなどを介して)、混乱を避けるためにエクスポートされていない複数の変数を保持します。(a;b)$(a)

答え3

このコマンドの出力は、locale現在の環境の環境変数のリストではありません。これは、コマンドkey=valueenv使用されるのと同じ形式で表示されるプロセス(特定の環境変数の影響を部分的に受け取る)の有効なロケールを表示します。

ここでeglibcコマンドの実装のソースコードを見ることができますlocalehttp://www.eglibc.org/cgi-bin/viewvc.cgi/branches/eglibc-2_19/libc/locale/programs/locale.c?view=markup

関連情報