シェル変数の範囲が明確でないことを示す問題が発生しました。
bundle install
を使用します。これは$GEM_HOME
の値を使用して作業を行うRubyコマンドです。ですが、export GEM_HOME=/some/path
のように、export
を使用するまで、コマンドはその値を無視しました。
これにより、変数が何らかの形で「グローバル」(環境変数とも呼ばれます)になることを読みましたが、そうではありません。それが何を意味するのか理解してください。プログラミングのグローバルについては知っていますが、個別のプログラム間では知りません。
また、このような変数の設定は現在のシェルセッションにのみ適用されるので、たとえばデーモン化されたプロセスにどのように設定しますか?
シェル変数はどのスコープを持つことができますか?
回答
プロセスはツリーとして編成されます。init
を除いてすべてのプロセスには一意の親がありますPID
は常に1であり、親はありません。
新しいプロセスの作成は、通常、fork
/ execv
システムコール。子プロセスの環境は、親プロセスのコピーです。
シェルから環境に変数を配置するには、その変数をexport
して、すべての子に再帰的に表示されるようにする必要があります。ただし、子が変数の値を変更した場合、変更された値はその子にのみ表示され、変更後に 作成されたすべてのプロセスは、以前のようにコピーであることに注意してください。
子プロセスが環境を変更する可能性があることも考慮に入れてください。たとえば、login
から行われるようにデフォルト値にリセットすることができます。例。
コメント
回答
At少なくともksh
とbash
の下では、変数は 3つスコープであり、残りのすべての回答が現在示しているように two ではありません。
Inエクスポートされた(つまり環境)変数とシェルのエクスポートされていない変数スコープに加えて、関数ローカル変数用に3番目に狭いスコープもあります。
typeset
トークンは、そこから呼び出される(サブ)関数で宣言されている関数内でのみ表示されます。
このksh
/ bash
コード:
# Create a shell script named /tmp/show that displays the scoped variables values. echo "echo [$environment] [$shell] [$local]" > /tmp/show chmod +x /tmp/show # Function local variable declaration function f { typeset local=three echo "in function": . /tmp/show } # Global variable declaration export environment=one # Unexported (i.e. local) variable declaration shell=two # Call the function that creates a function local variable and # display all three variable values from inside the function f # Display the three values from outside the function echo "in shell": . /tmp/show # Display the same values from a subshell echo "in subshell": /tmp/show # Display the same values from a disconnected shell (simulated here by a clean environment start) echo "in other shell" env -i /tmp/show
この出力を生成します:
in function: [one] [two] [three] in shell: [one] [two] [] in subshell: [one] [] [] in other shell [] [] []
ご覧のとおり、エクスポートされた変数は最初の3つの場所から表示され、エクスポートされていない変数は現在のシェルの外部には表示されず、関数のローカル変数は関数自体の外部には値がありません。最後のテストでは値がまったく表示されません。これは、エクスポートされた変数がシェル間で共有されないためです。つまり、継承のみが可能であり、継承された値は後で親シェルの影響を受けることはありません。
この後者の動作は、完全にグローバルですべてのプロセスで共有されるシステム変数を使用できるWindowsの動作とはまったく異なることに注意してください。
回答
プロセスによってスコープが設定されます
他の回答者は、シェル変数のスコープがプロセスに関するものであることを理解するのに役立ちましたおよびその子孫。
コマンドラインでls
のようなコマンドを入力すると、実際にはls
プログラムを実行するプロセスをフォークします。新しいプロセスの親としてシェルがあります。
どのプロセスにも、独自の「ローカル」変数を含めることができます。子プロセスには渡されません。「environment」変数を設定することもできます。export
を使用すると、環境変数が作成されます。どちらでも場合、無関係のプロセス(元のピア)は変数を認識しません。制御しているのはどの子プロセスのみですessessee。
Bashシェルがあるとします。これをAと呼びます。bash
と入力します。 、これは子プロセスのbashシェルを作成します。これを「B」と呼びます。Aでexport
と呼んだものはすべて、Bでも設定されます。
これで、 Bでは、FOO=b
と言います。次の2つのいずれかが発生します。
- Bが
FOO
という環境変数を(Aから)受信しなかった場合、ローカル変数が作成されます。 Bの子はそれを取得しません(Bがexport
を呼び出さない限り)。 - B がは(Aから)
FOO
と呼ばれる環境変数を受け取り、それ自体を変更してその後分岐した子。 Bの子には、Bが割り当てた値が表示されます。ただし、これはAにはまったく影響しません。
ここに簡単なデモがあります。
FOO=a # set "local" environment variable echo $FOO # "a" bash # forks a child process for the new shell echo $FOO # not set exit # return to original shell echo $FOO # still "a" export FOO # make FOO an environment variable bash # fork a new "child" shell echo $FOO # outputs "a" FOO=b # modifies environment (not local) variable bash # fork "grandchild" shell echo $FOO # outputs "b" exit # back to child shell exit # back to original shell echo $FOO # outputs "a"
これはすべて私の元の問題を説明しています:シェルにGEM_HOME
を設定しましたが、 bundle install
、子プロセスを作成しました。export
を使用していなかったため、子プロセスは「シェルを受信しませんでした」GEM_HOME
。
エクスポート解除
export -n FOO
。
export FOO=a # Set environment variable bash # fork a shell echo $FOO # outputs "a" export -n FOO # remove environment var for children bash # fork a shell echo $FOO # Not set exit # back up a level echo $FOO # outputs "a" - still a local variable
コメント
- “それ自体とその子のために変更します”変更後に作成した子のみであることを明確にする必要があります変更された値が表示されます。
- @ enzotib-良い点です。更新されました。
回答
エクスポートについて私が見つけることができる最も良い説明は次のとおりです:
http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_02.html
サブシェルまたは子シェル内に設定された変数は、それが定義されているサブシェル。エクスポートされた変数は、実際には環境変数になります。したがって、明確にするために、bundle install
は、iv id = “42dce32f9eにしない限り、$GEM_HOME
を認識しない独自のシェルを実行します。 “>
変数別名エクスポート。
変数スコープのドキュメントはこちらでご覧いただけます:
http://www.tldp.org/LDP/abs/html/subshells.html
コメント
- ああ、
環境変数”の
FOO=bar
;export
それを1つにします。それに応じて質問を修正しました。 - 追加したリンクを見てください。
回答
予想どおり、可変スコープの階層があります。
環境
最も外側のスコープは環境です。これが唯一のスコープです。オペレーティングシステムによって管理されるため、すべてのプロセスに存在することが保証されます。プロセスが開始されると、親の環境のコピー。その後、2つは独立します。子の環境を変更しても親の環境は変更されず、親の環境を変更しても既存の子の環境は変更されません。
シェル変数
シェルには独自の変数の概念があります。ここで少し混乱し始めます。
シェル内の変数に値を割り当て、その変数が環境にすでに存在する場合、環境変数は新しい値を受け取ります。ただし、変数がまだ環境内にない場合は、 shell 変数になります。 Ruby変数がRubyスクリプト内にのみ存在するのと同様に、シェル変数はシェルプロセス内にのみ存在します。それらは子プロセスに継承されることはありません。
ここでexport
キーワードが機能します。これは、シェル変数をシェルプロセスの環境にコピーして、子プロセスが継承できるようにします。
ローカル変数
ローカル変数は、それらを含むコードブロックをスコープとするシェル変数です。 ローカル変数は、typeset
キーワード(ポータブル)またはlocal
またはdeclare
(Bash)で宣言します。 )。 他のシェル変数と同様に、ローカル変数は子プロセスに継承されません。 また、ローカル変数はエクスポートできません。
FOO=bar
と言うと、現在のシェルプロセスの値が設定されます。次に(bundle install
)のようなプログラムを実行すると、子プロセスが作成され、’はFOO
。しかし、export FOO=bar
と言った場合、子プロセス(およびその子孫)はアクセスできます。そのうちの1つは、export FOO=buzz
を呼び出してその子孫の値を変更するか、FOO=buzz
を呼び出してそれ自体の値のみを変更することができます。 。