为啥当我在函数中声明一个名称为全局数组的局部数组时,bash 会引发未绑定变量警告?

Posted

技术标签:

【中文标题】为啥当我在函数中声明一个名称为全局数组的局部数组时,bash 会引发未绑定变量警告?【英文标题】:why bash throws unbound variable warning when I declare a local array in function whose name is shadowing a global one?为什么当我在函数中声明一个名称为全局数组的局部数组时,bash 会引发未绑定变量警告? 【发布时间】:2015-03-27 20:40:53 【问题描述】:

在这个例子中,声明与全局范围不同名称的局部变量不会产生错误,但是当名称与全局相同时,我得到:

line 5: !1: unbound variable

代码:

set -u
function get_arr 
  local myArr2=("$!1")
  echo $myArr2[*]
  local myArr=("$!1")
  echo $myArr[*]


myArr=(one two three)
get_arr myArr[@]

【问题讨论】:

你的 BASH 版本是多少? get_arr $myArr[@] 或引用发送所有元素get_arr "$myArr[@]" 我假设这是因为 local myArr 已经被处理,所以你的右手边现在指的是局部变量而不是全局变量。 @DavidC.Rankin:没有效果,两种选择都出现同样的错误 @DavidC.Rankin 这些都是将所有内容发送到函数的有效方式(后者显然更好),但这与 OPs 代码所做的不同。 【参考方案1】:

为了确保我们在同一张纸上,这里是在 Bash 3.2 上运行的版本(可以正常引用或不引用)。您的文件中必须包含与脚本无关的环境设置或杂散字符,从而导致问题:

#!/bin/bash

set -u
function get_arr 
    local myArr2=("$!1")
    echo $myArr2[*]
    local myArr=("$!1")
    echo $myArr[*]


myArr=(one two three)
get_arr "myArr[@]"

exit 0

版本

$ bash --version
GNU bash, version 3.2.39(1)-release (i586-suse-linux-gnu)
Copyright (C) 2007 Free Software Foundation, Inc.

输出

$ bash array_indirect_ref.sh
one two three
one two three

执行

$ bash -x array_indirect_ref.sh
+ set -u
+ myArr=(one two three)
+ get_arr 'myArr[@]'
+ myArr2=("$!1")
+ local myArr2
+ echo one two three
one two three
+ myArr=("$!1")
+ local myArr
+ echo one two three
one two three

【讨论】:

啊,是的,现在我已经从这里下载了 msys bash 3.2.51:lrn.no-ip.info/other/mingw/msys/bash - 这确实有效,所以整个问题是 3.1 中的一个错误。好吧,我不知道有 3.2 可用,google 或 msys 安装程序都给了我 3.1。 我也不知道它是否可用——我只是碰巧在办公室有一台运行 openSuSE 11.0 的旧传真服务器:p【参考方案2】:

更新:似乎你如何声明在你的函数中传递的数组会影响阴影名称是否会起作用,即使在新的 bash 版本中

上周我有一些 bash 代码可以正常工作,但在我将 cygwin 更新为当前代码后现在失败了。

~~~~~~~~~~~~~

我的 cygwin bash 版本现在是 4.3.39:

$ bash --version
GNU bash, version 4.3.39(2)-release (i686-pc-cygwin)

which is the latest.

~~~~~~~~~~~~~

考虑这个 bash 代码:

#!/bin/bash
set -e  # exit on first failed command
set -u  # exit if encounter never set variable

testArrayArg1() 
    declare -a argArray=("$!1")
    echo "testArrayArg1: $argArray[@]"


testArrayArg2() 
    declare -a anArray=("$!1")
    echo "testArrayArg2: $anArray[@]"


anArray=("a" "b" "c")

testArrayArg1 anArray[@]
testArrayArg2 anArray[@]

请注意,testArrayArg2 函数使用一个数组名称 (anArray),它会隐藏脚本中的后续变量名称。

还要注意,我将数组传递给函数的方式 (anArray[@]) 和我在函数中声明数组的方式 (declare -a anArray=("$!1")) 取自 Ken Bertelson 的回答 here。

上述两个功能过去都可以正常工作。

现在,在我的 cygwin/bash 更新后,testArrayArg1 仍然有效,但使用阴影数组名称的 testArrayArg2 失败:

$ bash t.sh
testArrayArg1: a b c
t.sh: line 11: !1: unbound variable

有人知道最近 bash 发生了什么变化导致了这种情况吗?

~~~~~~~~~~~~~

如果我将函数内的数组声明方式从 declare -a anArray=("$!1") 更改为 local anArray=("$!1") 的“本地”习语,我可以解决此问题。

所以,这段代码

testArrayArg3() 
    local anArray=("$!1")
    echo "testArrayArg3: $anArray[@]"


testArrayArg3 anArray[@]

作品:

testArrayArg3: a b c

~~~~~~~~~~~~~

好的,所以local anArray=("$!1") 函数数组 arg 声明习语似乎有效。

我在上面Ken Bertelson's answer 下的隐藏评论中提到的那个 SO 链接中提到了这个成语。要查看它,请点击“show 3 more”链接并查看 Mike Q 的评论。

它是否与declare -a anArray=("$!1") 成语一样好,还是有其自身的缺点?

我有一些关键代码依赖于将数组传递给 bash 函数,所以我真的需要弄清楚这一点。

【讨论】:

以上是关于为啥当我在函数中声明一个名称为全局数组的局部数组时,bash 会引发未绑定变量警告?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在c ++中声明一个全局数组时,它可以给出的大小大于在main中声明它[重复]

JS全局变量是全局对象的属性,函数局部变量为啥就不是函数的属性呢?

为啥 GLfloat 需要全局范围?

定义局部变量数组大小过大报错

为啥在函数中用作局部变量时数组不会沿堆栈方向增长?

为啥 malloc() 和普通数组声明分配的堆栈帧大小不同?