我在 bash 脚本编写中遇到的一件具有挑战性的事情是以递归方式使用变量扩展。
然而,我[通过 ChatGPT] 发现我可以使用间接替换来引用位置参数值:
linkerArgs+=("${!i}")
但是,如果我使用字符串替换,那么我可以按照逻辑上的方式以递归方式使用扩展:
"${x// /$newChar}"
为什么 bash 解释器在将项分配给数组的上下文中使用时将后一种类型的扩展(仅在括号内包含美元符号)视为“错误替换”,而不是在字符串替换中使用?
答案1
在替换中,您在语法的不同位置使用嵌入变量。它不是计算名称,而是计算参数。
这适用于经典的 POSIX 替换,例如${var#replacement}
.在里面replacement
,我们可以进行替换。
$ A=B
$ B=42
$ echo ${$A}
bash: ${$A}: bad substitution
$ echo ${!A}
42
但:
$ echo ${B}
42
$ echo ${B%2}
4
$ C=2
$ echo ${B%$C}
4
您可以看到它与语法中的$C
文字一样有效。2
${B%<tail-pattern-to-chop>}
语法${!A}
是 Bash 扩展。为什么不是显而易见的${$A}
?
很难推测。这是一个非常古老的 Bash 功能;它已经存在bash-2.0
于 1996 年的 tarball 中,该 tarball 是在当前 Bash git 存储库开始时导入的。
相比之下,Make 语言对计算变量做了显而易见的事情,例如$(OBJS_$(this_file))
.
这纯粹是猜测,但这可能就是问题所在。如果我们查看 Make 中的计算变量,它们并不限于$($(...
.的内部$(...)
可以在任何地方包含变量。
如果有人发现它${$A}
在 shell 中工作,其中A
被替换为B
,则期望类似的东西也可以通过计算和扩展变量${abc_$A}
来工作。abc_B
因此,可能,实施者/发明者${!A}
不愿意走那么远。
另一种可能性是其他${!
功能${!name[@]}
在该功能之前已经存在;因此它是基于该语法的,${!word}
当word
后面不跟@
、*
或[*]
时赋予了含义[@]
。将新扩展融入现有扩展的语法中很容易,因为它减少了对可能破坏的内容的推理量;只有使用现有扩展名的程序才会受到影响。
不幸的是,GNU 的 FTP 存储库只提供了bash-1.14.7.tar.gz
tarball 和bash-2.0.tar.gz
tarball。两者之间没有什么。 1.14.7版本没有形式上的扩展${!...}
。就此而言,它还没有实现数组。 2.0 版本实现了所有${!...}
语法:${!word}
以及${name[@]}
等等。我们没有详细的历史记录来判断哪个先发生。
作为最后的手段,我们可以采访切特·雷米。我怀疑答案可能是这样的:多个扩展被添加到变量扩展中,间接引用只是被固定到相同的语法中,而不是获得自己的语法。即,当 Bash 看到的${!
意思是“在此之后处理各种 Bash 扩展”。