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:]

问:这是我的 shell 中的错误(bash 4.3.42、Ubuntu 16.04、64 位)还是预期的行为?

编辑:解决了。正如@MarkPlotnick 所指出的,这确实是预期的行为。

答案1

Shell 变量有一个动态范围。如果变量被声明为函数的局部变量,则该范围将一直有效,直到函数返回为止,包括在调用其他函数期间

这与大多数编程语言形成鲜明对比词汇范围。 Perl 两者都有:my对于词法范围,local或者没有动态范围的声明。

有两个例外:

  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'声明为当地的,因此它在全局范围内不可用(即使该函数已被调用)。

因为varinnerFunc()被作为 的子级调用outerFunc(),所以位于 的本地范围内outerFunc()

man 1 bash可能有助于澄清

本地 [选项] [名称[=值] ...]

对于每个参数,都会创建一个名为 name 的局部变量,并分配值。该选项可以是声明接受的任何选项。当在函数中使用 local 时,它会导致变量名称的可见范围仅限于该函数及其子函数。 ...

描述中预期的隐含行为可以通过local var='new value在 中声明来实现function innerFunc()

正如其他人所说,这不是 bash shell 中的错误。一切都按其应有的方式运作。

答案3

这不是一个错误,outerFunc 上下文中的调用使用 $var 的本地副本。 outerFunc 中的“local”意味着全局不会改变。如果你在outerFunc之外调用innerFunc,那么全局$var将会发生变化,但outerFunc的本地$var不会发生变化。如果你将“local”添加到innerFunc,那么outerFunc的$var就不会改变——本质上,会有3个:

  • $全局::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}]"

相关内容