シェル変数と環境変数の使用法の違いは何ですか?


16

実際、コマンドラインからアクセスできる変数には2種類あることを知りませんでした。私が知っているのは、次のような変数を宣言できることです。

foo="my dear friends"
bar[0]="one"
bar[1]="two"
bar[2]="three"

または、次のような$記号を使用してアクセスします。

echo $foo
echo ${bar[1]}

または、次のような組み込み変数を使用します。

echo $PWD
PATH=$PATH:"/usr/bin/myProg"

さて、シェル変数と環境変数という2つの(少なくとも?)タイプの変数があると聞きました。

  • 2つの異なるタイプを使用する目的は何ですか?
  • 変数がどの型であるかを知るにはどうすればよいですか?
  • それぞれの典型的な使用法は何ですか?


回答:


14

環境変数はname=value、プログラム(シェル、アプリケーション、デーモンなど)に関係なく存在するペアのリストです。通常、子プロセス(fork/ execシーケンスで作成)によって継承されます。子プロセスは、親変数の独自のコピーを取得します。

シェル変数は、シェルのコンテキストにのみ存在します。サブシェルでのみ継承されます(つまり、シェルがexec操作なしでフォークされた場合)。シェルの機能に応じて、変数は環境変数などの単純な文字列だけでなく、配列、複合、整数や浮動小数点などの型付き変数などもあります。

シェルが起動すると、親から継承するすべての環境変数もシェル変数になります(シェル変数やIFS一部のシェルによってリセットされるような他のコーナーケースとして無効でない限り)が、これらの継承された変数はexport 1としてタグ付けされます。つまり、シェルによって設定された潜在的に更新された値を使用して、子プロセスで使用可能なままになります。これは、シェルで作成され、exportキーワードでエクスポートされたものとしてタグ付けされた変数の場合にも当てはまります。

自分の名前と値をに変換することができない限り、配列やその他の複雑な型変数をエクスポートすることはできませんname=value:パターン、またはシェルの特定のメカニズムが所定の位置にあるとき(例えばbash環境での輸出機能など、いくつかのエキゾチックな、非POSIXシェルrces配列をエクスポートすることができます)。

したがって、環境変数とシェル変数の主な違いはスコープです。環境変数はグローバルですが、エクスポートされていないシェル変数はスクリプトに対してローカルです。

また、最新のシェル(kshおよびbash)が3番目のシェル変数スコープをサポートしていることにも注意してください。関数で作成された変数typesetキーワードは、(関数が宣言されている方法は、下に、この機能を有効/無効にし、その関数に対してローカルでありksh、かつ持続性の動作が間で異なるbashksh)。/unix//a/28349/2594を参照してください

1 これは、現代のようなシェルに適用されkshdashbashおよび同様。従来のBourneシェルと非Bourne構文シェルのcsh動作は異なります。


1
子は親のフォーク(正確なコピー)として作成されるため、すべてが子プロセスに継承されます。環境変数のポイントは、それらがexecve()システムコールに渡されるため、(通常)(同じプロセスで)他のコマンドの実行中にデータを永続化するために使用されることです。
ステファンシャゼラス

すべての環境変数がシェル変数に変換されるわけではありません。シェル変数名として有効なもののみ(およびIFSいくつかのシェルのようにいくつかの例外があります)。
ステファンシャゼル

のようなシェルはrcesアドホックエンコーディングを使用して配列をエクスポートできます。bashそしてrcまた、(特殊なエンコーディングを使用して、再び)環境変数を使用して関数をエクスポートすることができます。
ステファンシャゼラス

ではksh93、Bourne()構文ではなくtypesetfunction foo { ...; }構文で宣言された関数でのみスコープを制限しfoo() cmdます(他のシェルのように動的スコープではなく静的スコープです)。
ステファンシャゼラス

@StéphaneChazelasレビューありがとうございます!コメントを考慮して返信を更新しました。
-jlliagre

17

シェル変数

シェル変数は、スコープが現在のシェルセッション(対話型シェルセッションやスクリプトなど)にある変数です。

未使用の名前に値を割り当てることにより、シェル変数を作成できます。

var="hello"

シェル変数の使用は、現在のセッションのデータを追跡することです。シェル変数には通常、小文字の名前が付いています。

環境変数

環境変数は、エクスポートされたシェル変数です。つまり、変数として作成されたシェルセッションだけでなく、そのセッションから開始されたプロセス(シェルだけでなく)でも変数として表示されます。

VAR="hello"  # shell variable created
export VAR   # variable now part of the environment

または

export VAR="hello"

シェル変数がエクスポートされると、設定が解除されるまで、またはその「エクスポートプロパティ」が(export -ninでbash)削除されるまでエクスポートされたままなので、通常は再エクスポートする必要はありません。変数を設定解除すると、unsetそれが削除されます(環境変数であるかどうかは関係ありません)。

配列および連想ハッシュbashおよびその他のシェルは、環境変数になるためにエクスポートされない場合があります。環境変数は、値が文字列である単純な変数である必要があり、多くの場合、名前は大文字で構成されています。

環境変数を使用すると、現在のシェルセッションのデータを追跡するだけでなく、開始されたプロセスがそのデータの一部を取ることができます。この典型的なケースはPATH環境変数です。これはシェルで設定され、後でプログラムへのフルパスを指定せずにプログラムを起動したい任意のプログラムで使用されます。

プロセス内の環境変数のコレクションは、多くの場合「プロセスの環境」と呼ばれます。各プロセスには独自の環境があります。

環境変数は「転送」のみ可能です。つまり、子プロセスは親プロセスの環境変数を変更できません。また、起動時に子プロセスの環境を設定する以外は、親プロセスは既存の環境を変更できません。子プロセス。

環境変数はenv(引数なしで)リストできます。それ以外は、シェルセッションではエクスポートされていないシェル変数と同じように見えます。他のほとんどのプログラミング言語は通常「通常の」変数と環境変数を混在させないため、これはシェルにとって少し特別です(以下を参照)。

env また、現在のセッションで設定せずに、プロセスの環境で1つまたは複数の環境変数の値を設定するために使用できます。

env CC=clang CXX=clang++ make

これは、開始make、環境変数とCC値に設定clangしてCXXに設定clang++

プロセスの環境をクリアするためにも使用できます。

env -i bash

これは開始されますbashが、現在の環境を新しいbashプロセスに転送しません(シェル初期化スクリプトから新しい環境変数を作成するため、環境変数はまだあります)。

違いの例

$ var="hello"   # create shell variable "var"
$ bash          # start _new_ bash session
$ echo "$var"   # no output
$ exit          # back to original shell session
$ echo "$var"   # "hello" is outputted
$ unset var     # remove variable

$ export VAR="hello"  # create environment variable "VAR"
$ bash
$ echo "$VAR"         # "hello" is outputted since it's exported
$ exit                # back to original shell session
$ unset VAR           # remove variable

$ ( export VAR="hello"; echo "$VAR" )  # set env. var "VAR" to "hello" in subshell and echo it
$ echo "$VAR"         # no output since a subshell has its own environment

他の言語

ほとんどのプログラミング言語には、環境変数の取得と設定を可能にするライブラリ関数があります。環境変数は単純なキーと値の関係として保存されるため、通常は言語の「変数」ではありません。プログラムは、キー(環境変数の名前)に対応する値(常に文字列)を取得できますが、その値を整数または言語が値に期待するデータ型に変換する必要があります。

Cは、環境変数を使用してアクセスすることができるgetenv()setenv()putenv()およびunsetenv()。これらのルーチンで作成された変数は、Cプログラムが開始するプロセスによって同じ方法で継承されます。

他の言語には%ENV、Perl のハッシュや、ENVIRONほとんどの実装の連想配列など、同じことを達成するための特別なデータ構造がありawkます。


thx、見事に明確な説明。したがって、環境は、他のプログラムが生きて各環境変数を見ることができる大きなフィールドのようなものです。一部のプログラムにはプライベート変数があり、シェルのように自分自身だけが見ることができます。しかし、プライベート変数をすべて「エクスポート」と呼ばれるものにするメカニズムがあります。これがわかっていれば、私が確信していないのは、同時に複数の環境が存在できるかどうかだけです。
-sharkant

@sharkant実行中のプロセスにはそれぞれ独自の環境があります。この環境は、それを開始したプロセスから継承されます。異なるプロセスの環境間で「クロストーク」はありません。プロセス内の環境変数を変更する唯一の方法は、プロセス自体がそれを変更することです。
クサラナナンダ

理解を深めるためのthx。独自の魚ボウル内の各魚。他のプロセスを生成するプロセスはどうですか?プロセスとその子プロセスはすべて1つの環境内にありますか、それとも独自の環境がありますか?
-sharkant

1
@sharkantほとんどの言語には、環境変数の取得と設定を可能にするライブラリ関数があります。Cでは、これはで行われgetenv()setenv()putenv()unsetenv()。これらのルーチンで作成された変数は、Cプログラムが開始するプロセスによって同じ方法で継承されます。他の言語は%ENV、Perlのように、同じもののための特別なデータ構造を持つ場合があります。
クサラナナンダ

1
FWIW:exec*()関数ファミリーは、実行中のプロセスの環境を設定することもできます。
桂さとう

5

シェル変数の複製は困難です。

$ FOO=bar
$ FOO=zot
$ echo $FOO
zot
$ 

ただし、環境変数は複製できます。それらは単なるリストであり、リストには重複したエントリを含めることができます。ここだenvdup.cちょうどそれをします。

#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

extern char **environ;

int main(int argc, char *argv[]) {
    char **newenv;
    int envcount = 0;

    if (argc < 2) errx(64, "Usage: envdup command [args ..]");

    newenv = environ;
    while (*newenv++ != NULL) envcount++;

    newenv = malloc(sizeof(char *) * (envcount + 3));
    if (newenv == NULL) err(1, "malloc failed");
    memcpy(newenv, environ, sizeof(char *) * envcount);
    newenv[envcount]   = "FOO=bar";
    newenv[envcount+1] = "FOO=zot";
    newenv[envcount+2] = NULL;

    environ = newenv;
    argv++;
    execvp(*argv, argv);
    err(1, "exec failed '%s'", *argv);
}

どの環境変数が設定されているかを示すenvdupために、コンパイルして実行してから実行することができますenv...

$ make envdup
cc     envdup.c   -o envdup
$ unset FOO
$ ./envdup env | grep FOO
FOO=bar
FOO=zot
$ 

これはおそらくプログラムがうまく処理できるバグやその他の奇妙なものを見つけるのにのみ役立ち**environます。

$ unset FOO
$ ./envdup perl -e 'exec "env"' | grep FOO
FOO=bar
$ ./envdup python3 -c 'import os;os.execvp("env",["env"])' | grep FOO
FOO=bar
FOO=zot
$ 

ここではPython 3.6が盲目的に重複を通過しているように見えますが(漏れやすい抽象化)、Perl 5.24は通過しません。シェルはどうですか?

$ ./envdup bash -c 'echo $FOO; exec env' | egrep 'bar|zot'
zot
FOO=zot
$ ./envdup zsh -c 'echo $FOO; exec env' | egrep 'bar|zot' 
bar
FOO=bar
$ 

場合まあ、何が起こるsudoだけで最初の環境エントリをサニタイズが、その後bash第二で実行されますか?こんにちはPATHまたはLD_RUN_PATH悪用。あなたsudo(そして他のすべてのものはその穴にパッチを当てていますか?セキュリティの悪用は、呼び出しプログラムの「逸話的な違い」でも「単なるバグ」でもありません。


1
それは事実ですが、逸話的な違いであり、おそらく、重複変数を設定するプログラムのバグです。
-jlliagre


0

環境変数は似ているシェル変数が、それは固有ではありませんシェルUnixシステム上のすべてのプロセスには、環境変数ストレージがあります。 環境変数とシェル変数の主な違いはオペレーティングシステムがシェルのすべての環境変数をシェルが実行するプログラムに渡すのに対して、実行するコマンドではシェル変数にアクセスできないことです。

env –このコマンドを使用すると、現在の環境を変更せずに、カスタム環境で別のプログラムを実行できます。引数なしで使用すると、現在の環境変数のリストが出力されます。 printenv –このコマンドは、すべてまたは指定された環境変数を出力します。 set –このコマンドは、シェル変数を設定または設定解除します。引数なしで使用すると、環境変数とシェル変数、およびシェル関数を含むすべての変数のリストが出力されます。 unset –このコマンドは、シェル変数と環境変数を削除します。 export –このコマンドは環境変数を設定します

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.