使 nameref 成为 bash 中的常规变量

Making a nameref a regular variable in bash

编辑: 这已被确认为一个错误,将被修复:https://lists.gnu.org/archive/html/bug-bash/2018-03/msg00055.html


所以我在摆弄 bash 的间接功能 namerefs。我以为我已经掌握了它的窍门,但是当我试图弄清楚如何将 namerefs 变成常规变量时,我偶然发现了一些让我困惑的事情。

这是来自 man bash 的相关段落:

declare -n Give each name the nameref attribute, making it a name reference to another variable. That other variable is defined by the value of name. All references, assignments, and attribute modifications to name, except those using or changing the -n attribute itself, are performed on the variable referenced by name's value.


我理解的部分

假设您想取消设置一个名称引用——不是它指向的变量,而是变量本身。您不能只说 unset foo,因为这实际上会取消 foo 指向的任何内容;相反,您必须将其设为常规变量,然后取消设置:

$ declare -p

$ foo=bar; bar='hello world'

$ declare -p
declare -- foo="bar"
declare -- bar="hello world"

$ declare -n foo; declare -p    # 'foo' is now a nameref
declare -n foo="bar"
declare -- bar="hello world"

$ declare +n foo; declare -p    # 'foo' is no longer a nameref
declare -- foo="bar"
declare -- bar="hello world"

$ unset foo; declare -p         # 'foo' is unset, not bar
declare -- bar="hello world"

部分我不明白

这一切对我来说都是有意义的,并且与我对上述手册段落的阅读是一致的。令我困惑的是上面的一个小变化会发生什么——也就是说,我们让 bar 未设置和未声明:

...
$ declare -p
declare -n foo="bar"

$ echo "${foo}"                 # These two commands behave as expected--i.e., identically to how namerefs usually behave, just with an unset variable.
-bash: foo: unbound variable

$ echo "${!foo}"
bar

$ declare +n foo; declare -p    # Should make 'foo' a regular variable, right? Nope.
declare -n foo="bar"            # Still a nameref--wtf?
declare -- bar                  # And now bar's back--unset still, but declared. Wtf??

$ declare +n foo; declare -p    # THIS, however, works like I thought it would--but *why*? In both cases 'bar' is unset...
declare -- foo="bar"
declare -- bar

我显然误解了 nameref 应该如何工作。根据 man 的文章,我认为取消设置 foo 的 nameref 属性应该适用于 foo,无论其目标 bar 是否未声明。

请注意,当 bar 未设置但已声明时,它的工作方式与我想象的一样。这对我来说是最奇怪的部分——我没有意识到未声明的变量有任何意义! test -v${var-_}${var+_}set -u似乎都只关心变量是不是set,不做任何区分在 (A) 一个未设置的、未声明的变量和 (B) 一个未设置的、声明的变量之间。

谁能解释一下这里发生了什么,也许可以指出手册中对此进行解释的部分?在 namerefs 的行为中是否还有其他特殊情况会让我感到困惑?谢谢!


可能相关的信息:

$ bash --version
GNU bash, version 4.4.19(1)-release (x86_64-unknown-linux-gnu)
...

$ echo "$-"
himuBCHs

请注意,该行为在没有 set -u 的情况下仍然存在;我这样做只是为了让 bash 的信息更清晰一些。

unset 有一个新参数明确用于取消定义 nameref(与它指向的变量相反):

unset -n namevar