Shell函数中局部变量的范围


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

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

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


这是预期的行为
fpmurphy's

2
我是唯一一个认为输出的最后一行的值为var空很奇怪的人吗?var是在中全局设置的innerFunc,那么为什么不一直坚持到脚本结束?
哈罗德·菲舍尔

Answers:


22

Shell变量具有动态范围。如果将变量声明为函数的局部变量,则该作用域将一直保留到函数返回为止。

有两个例外:

  1. 在ksh93中,如果使用标准function_name () { … }语法定义了函数,则其局部变量服从动态作用域。但是,如果使用ksh语法定义函数,function function_name { … }则其局部变量服从词法/静态作用域,因此在此函数调用的其他函数中不可见。

  2. 中的zsh/privateautoloadable插件zsh提供了private关键字/内建函数,可用于声明具有静态作用域的变量。

ash,bash,pdksh及其衍生物,bosh仅具有动态作用域。


shell中的所有变量是否都具有动态作用域,或者这仅适用于用声明的变量local
哈罗德·菲舍尔

@HaroldFischer所有变量都有动态范围。用typesetdeclarelocal声明,作用域将一直持续到函数返回。没有这样的声明,范围是全球性的。
吉尔斯(Gillles)“所以别再邪恶了”

6

这不是一个错误,在externalFunc上下文中的调用使用$ var的本地副本。externalFunc中的“本地”表示全局未更改。如果在outerFunc之外调用innerFunc,则将更改全局$ var,但不会更改outerFunc的本地$ var。如果您将“ local”添加到innerFunc,则externalFunc的$ 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'被声明为 local,因此它在全局范围内不可用(即使已调用该函数)。

因为innerFunc()被称为孩子outerFunc(),因此var在的本地范围内outerFunc()

man 1 bash 可能有助于澄清

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

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

这是一个在描述预期隐含的行为可以通过声明来实现local var='new valuefunction innerFunc()

正如其他人所说,这不是bash shell中的错误。一切运转正常。


您的第一条陈述与用户看到的内容矛盾。打印的价值var在全球范围内,调用后innerFunc通过outFunc,不打印new value
库沙兰丹
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.