使 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
编辑: 这已被确认为一个错误,将被修复: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