![Shell 函数中局部变量的作用域](https://linux22.com/image/88138/Shell%20%E5%87%BD%E6%95%B0%E4%B8%AD%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E7%9A%84%E4%BD%9C%E7%94%A8%E5%9F%9F.png)
看完之后24.2.局部变量,我认为var
用关键字声明变量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:]
问:这是我的 shell 中的错误(bash 4.3.42、Ubuntu 16.04、64 位)还是预期的行为?
编辑:解决了。正如@MarkPlotnick 所指出的,这确实是预期的行为。
答案1
Shell 变量有一个动态范围。如果变量被声明为函数的局部变量,则该范围将一直有效,直到函数返回为止,包括在调用其他函数期间!
这与大多数编程语言形成鲜明对比词汇范围。 Perl 两者都有:my
对于词法范围,local
或者没有动态范围的声明。
有两个例外:
在 ksh93 中,如果使用标准语法定义函数
function_name () { … }
,则其局部变量遵循动态作用域。但是,如果使用 ksh 语法定义函数,function function_name { … }
则其局部变量遵循词法/静态作用域,因此它们在由此调用的其他函数中不可见。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}]"