シェル関数のローカル変数の範囲


28

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、64bit)のバグですか、それとも予想される動作ですか?

編集:解決しました。@MarkPlotnickで述べたように、これは実際に予想される動作です。


それは予想される動作です
-fpmurphy

2
出力の最後の行での値varが空であるとおかしいと思うのは私だけですか?varでグローバルに設定されているinnerFuncので、なぜスクリプトの最後まで固定されないのですか?
ハロルドフィッシャー

回答:


22

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

2つの例外があります。

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

  2. zsh/private自動ロードプラグインzshで提供privateキーワード/組み込み静的スコープを持つ変数を宣言するために使用することができます。

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


シェル内のすべての変数は動的スコープを持っていますか、またはこれはlocal?で宣言された変数にのみ適用されますか?
ハロルドフィッシャー

@HaroldFischerすべての変数には動的スコープがあります。でtypesetまたはdeclareまたはlocal宣言、スコープは関数が戻るまでです。そのような宣言がなければ、スコープはグローバルです。
ジル「SO-悪であるのをやめなさい」

6

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

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

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


2

関数を使用して、ローカルスコープを強制できます。

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]

ソース


2

ではとして宣言されていない地元、したがって、それは(関数が呼び出された後)は、可視範囲で利用可能です。 function innerFunc()var='new value'

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

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

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

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

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

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

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


最初のステートメントは、ユーザーが見ているものと矛盾しています。をvar呼び出した後、グローバルスコープのの値を印刷しinnerFuncてもoutFunc、は印刷されませんnew value
クサラナナンダ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.