シェル変数はどのスコープを持つことができますか?

シェル変数の範囲が明確でないことを示す問題が発生しました。

bundle installを使用します。これは$GEM_HOMEの値を使用して作業を行うRubyコマンドです。ですが、export GEM_HOME=/some/pathのように、exportを使用するまで、コマンドはその値を無視しました。

これにより、変数が何らかの形で「グローバル」(環境変数とも呼ばれます)になることを読みましたが、そうではありません。それが何を意味するのか理解してください。プログラミングのグローバルについては知っていますが、個別のプログラム間では知りません。

また、このような変数の設定は現在のシェルセッションにのみ適用されるので、たとえばデーモン化されたプロセスにどのように設定しますか?

シェル変数はどのスコープを持つことができますか?

回答

プロセスはツリーとして編成されます。initを除いてすべてのプロセスには一意の親がありますPIDは常に1であり、親はありません。

新しいプロセスの作成は、通常、fork / execvシステムコール。子プロセスの環境は、親プロセスのコピーです。

シェルから環境に変数を配置するには、その変数をexportして、すべての子に再帰的に表示されるようにする必要があります。ただし、子が変数の値を変更した場合、変更された値はその子にのみ表示され、変更後に 作成されたすべてのプロセスは、以前のようにコピーであることに注意してください。

子プロセスが環境を変更する可能性があることも考慮に入れてください。たとえば、loginから行われるようにデフォルト値にリセットすることができます。例。

コメント

  • ああ! OK、’がこれを理解しているかどうかを確認しましょう。シェルで、FOO=barと言うと、現在のシェルプロセスの値が設定されます。次に(bundle install)のようなプログラムを実行すると、子プロセスが作成され、’はFOO。しかし、export FOO=barと言った場合、子プロセス(およびその子孫)はアクセスできます。そのうちの1つは、export FOO=buzzを呼び出してその子孫の値を変更するか、FOO=buzzを呼び出してそれ自体の値のみを変更することができます。 。
  • @NathanLong That ‘は正確ではありません。最近のすべてのシェルでは、変数がエクスポートされます(したがって、値の変更は次のようになります。子孫の環境に反映される)またはエクスポートされない(変数が環境にないことを意味します)。特に、シェルの起動時に変数がすでに環境内にある場合は、エクスポートされます。
  • 子の場合は”という文に少し混乱しました。変数の値を変更すると、変更された値はその変数と、その変更後に作成されたすべてのプロセスにのみ表示されます”。 ” …表示され、その変更後に作成されたすべての子孫プロセス” –その他と言う方が正しいでしょう。親プロセスの子は、子プロセスの後に開始されたものであっても、影響を受けません。

回答

At少なくともkshbashの下では、変数は 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)で宣言します。 )。 他のシェル変数と同様に、ローカル変数は子プロセスに継承されません。 また、ローカル変数はエクスポートできません。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です