Shell関数のローカル変数の範囲

Shell関数のローカル変数の範囲

読んだ後24.2。ローカル変数var、キーワードを使用して変数を宣言することは、関数の中かっこで区切られたコードブロック内でのみその値にアクセスできることlocalを意味すると思いました。var

varしかし、次の例を実行した後、このコードブロックで呼び出された関数からアクセス、読み書きすることができることがわかりました。つまり、 とvar宣言された場合でも依然として読み取り変更することができます。値。localouterFuncinnerFunc

Run It Online

#!/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が指摘したように、これは実際に予想される動作です。

答え1

シェル変数にダイナミックレンジ。変数が関数に対してローカルに宣言されている場合、その範囲は関数が返されるまで有効です。他の関数の呼び出し中に含める

これはほとんどのプログラミング言語とは明らかに対照的です。語彙範囲。 Perl には次の両方があります。my語彙範囲の場合、localまたは動的範囲のない宣言です。

2つの例外があります。

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

  2. zsh/private自動ロードプラグインは、静的範囲で変数を宣言するために使用できるキーワード/組み込み関数を提供zshしますprivate

ash、bash、pdksh、およびその派生物であるboshにはダイナミックレンジしかありません。

答え2

function innerFunc()その中でvar='new value'次のように宣言しました。地元のそのため、可視範囲(関数呼び出し後)で使用できます。

function outerFunc()代わりに宣言では、local var='initial value'地元のしたがって、関数が呼び出されてもグローバルに使用することはできません。

varはの子innerFunc()として呼び出されるので、outerFunc()そのローカル範囲に属しますouterFunc()

man 1 bash明確にするのに役立ちます

地域[オプション][名前[=値] ...]

各パラメータに対して、nameというローカル変数が生成され、値が割り当てられます。オプションは、許容可能であると宣言された任意のオプションにすることができます。関数内でローカルを使用すると、変数名はその関数とそのサブ関数にのみ表示されます。 ...

説明で予想される暗黙の動作は、で宣言することlocal var='new valueで達成できますfunction innerFunc()

他の人が言ったように、これはbashシェルのバグではありません。すべてが期待どおりに動作します。

答え3

これはバグではありません。 externalFunc コンテキストの呼び出しは $var のローカルコピーを使用します。 OuterFuncの「local」は、グローバルが変更されないことを意味します。 externalFuncの外部でinnerFuncを呼び出すと、グローバル$ varは変更されますが、externalFuncのローカル$ varは変更されません。 innerFuncに "local"を追加すると、externalFuncの$ varは変更されません。基本的に3つになります。

  • $global::var
  • $outerFunc::var
  • $innerFunc::var

一種のPerlの名前空間形式を使用します。

答え4

これは予想される動作です。ローカル変数にはダイナミックレンジがあります。変数は、宣言された関数が返されるまで範囲内に保持されます。したがって、これらの変数は、この関数で呼び出されるすべての関数の範囲に含まれます。

関数が他の(ローカルまたはグローバル)変数と同じ名前の新しいローカル変数を宣言すると、新しいローカル変数は別のメモリ領域にあり、同じ名前を持つ他の変数とは異なる値を保持できます。

関数内でローカル宣言が現れる場所は重要ではありません。

例として提供されたコードを少し修正しました。

#!/usr/bin/env bash

function innerFunc() {
    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}]"
innerFunc
echo "global:    after  outerFunc: [var:${var}]"

関連情報