シェル関数のローカル変数のスコープ

24.2を読んだ後。ローカル変数、キーワードlocalを使用して変数varを宣言すると、var “の値は、関数の中括弧で区切られたコードのブロック内でのみアクセス可能でした。

しかし、次の例を実行した後、は、そのコードブロックによって呼び出される関数からアクセス、読み取り、書き込みすることもできます。つまり、varが宣言されている場合でもlocalからouterFuncまで、innerFuncは引き続きそれを読み取って値を変更できます。

オンラインで実行

#!/usr/bin/env bash function innerFunc() { var="new value" echo "innerFunc: [var:${var}]" } function outerFunc() { local var="initial value" echo "outerFunc: before innerFunc: [var:${var}]" innerFunc echo "outerFunc: after innerFunc: [var:${var}]" } echo "global: before outerFunc: [var:${var}]" outerFunc echo "global: after outerFunc: [var:${var}]" 

出力:

global: before outerFunc: [var:] # as expected, `var` is not accessible outside of `outerFunc` outerFunc: before innerFunc: [var:initial value] innerFunc: [var:new value] # `innerFunc` has access to `var` ?? outerFunc: after innerFunc: [var:new value] # the modification of `var` by `innerFunc` is visible to `outerFunc` ?? global: after outerFunc: [var:] 

Q :それは私のシェル(bash 4.3.42、Ubuntu 16.04、64ビット)のバグですか、それとも予想される動作ですか?

編集:解決しました。 @MarkPlotnickが指摘しているように、これは確かに予想される動作です。

コメント

  • 予想される動作です
  • Am I 'の出力の最後の行で、varの値が空であると考えるのは、唯一の人ですか? varinnerFuncでグローバルに設定されているのに、なぜ'最後まで固執しないのですかスクリプトの?

回答

シェル変数には動的スコープ。変数が関数に対してローカルとして宣言されている場合、そのスコープは、他の関数の呼び出し中も含め、関数が戻るまで残ります。

2つの例外があります。

  1. ksh93では、関数が標準のfunction_name () { … }構文で定義されている場合、そのローカル変数は動的スコープに従います。ただし、関数がksh構文function function_name { … }で定義されている場合、そのローカル変数は字句/静的スコープに従うため、これによって呼び出される他の関数では表示されません。

  2. zshzsh/private自動ロード可能プラグインはprivate静的スコープで変数を宣言するために使用できるkeyword / builtin。

ash、bash、pdksh、および派生物、boshには動的スコープのみがあります。

コメント

  • シェル内のすべての変数に動的スコープがありますか、それともlocalで宣言された変数にのみ適用されますか?
  • @HaroldFischerすべての変数には動的スコープがあります。 typesetまたはdeclareまたはlocal宣言の場合、スコープは関数が戻るまでです。このような宣言がない場合、スコープはグローバルです。
  • IMHO、If a variable is declared as local to a function, that scope remains until the function returns.は、動的スコープと字句スコープを説明するのに十分ではありません。説明だけが字句スコープにも適用されます。
  • @jinbeomhongいいえ、字句スコープでは、この関数が別の関数を呼び出している間、関数の変数は表示されません。 'これを明示的に示す文を追加しました。
  • これは関数呼び出しビルトインにも影響しますか?または、ビルトインには独自のスコープがありますか?

回答

これはバグではなく、コンテキスト内の呼び出しです。のouterFuncは、$ varのローカルコピーを使用します。outerFuncの「ローカル」は、グローバルが変更されていないことを意味します。 externalFuncの外部でinnerFuncを呼び出すと、グローバル$ varは変更されますが、outerFuncのローカル$ varは変更されません。innerFuncに「local」を追加した場合、outerFuncの$ varは変更されません。 -本質的には、次の3つがあります:

  • $ global :: var
  • $ outsideFunc :: var
  • $ innerFunc :: var

Perlの名前空間形式を使用するには、ある種。

回答

function innerFunc()では、var="new value" local として宣言されていなかったため、可視スコープで使用できます(1回)関数が呼び出されました)。

逆に、function outerFunc()では、local var="initial value" local <として宣言されました。 / em>、したがって、グローバルスコープでは使用できません(関数が呼び出された場合でも)。

innerFunc()がの子として呼び出されたためouterFunc()、varはouterFunc()のローカルスコープ内にあります。

man 1 bash明確にするのに役立つかもしれません

ローカル[オプション] [名前[=値] …]

各引数について、ローカルnameという名前の変数が作成され、値が割り当てられます。オプションは、declareによって受け入れられたオプションのいずれかです。関数内でlocalを使用すると、変数名のスコープがその関数とその子に制限されます。 …

説明で期待される暗黙の動作は、でlocal var="new valueを宣言することで実現できます。 function innerFunc()

他の人が述べているように、これはbashシェルのバグではありません。すべてが正常に機能しています。

コメント

  • 最初のステートメントは、ユーザーに表示されている内容と矛盾します。 outFuncを介してinnerFuncを呼び出した後、グローバルスコープでvarの値を出力すると印刷しないnew value

回答

ローカルスコープを強制する関数:

sh_local() { eval "$(set)" command eval "\"\$@\"" } 

例:

x() { z="new value" printf "function x, z = [%s]\n" "$z" } y() { z="initial value" printf "function y before x, z = [%s]\n" "$z" sh_local x printf "function y after x, z = [%s]\n" "$z" } printf "global before y, z = [%s]\n" "$z" y printf "global after y, z = [%s]\n" "$z" 

結果:

global before y, z = [] function y before x, z = [initial value] function x, z = [new value] function y after x, z = [initial value] global after y, z = [initial value] 

ソース

コメントを残す

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