说:
variable="Something that it holds"
然后
echo "$variable"
将输出: 它持有的东西
但说我也这样做:
var2="variable";
echo "\$$(echo $var2)"
只会输出:$variable
而不是: 它所拥有的东西
谁能告诉我 Unix 的哪些功能在这里发挥作用?
答案1
variable="Something" var2="variable"; echo "\$$(echo $var2)"
在最后一行中,您期望"\$$(echo $var2)"
→ $variable
→ Something
。这需要 shell 执行两次参数扩展。它不会,而是简单地打印$(echo $var2)
前缀为 的命令替换的结果$
。
原则,评估帮助你得到你想要的。 shell 执行第一步"\$$(echo $var2)"
→后$variable
, eval 执行第二步$variable
→ Something
。
$ eval echo "\$$(echo $var2)"
Something
尽管在我们的特定情况下上述命令是可以的,但仍然缺乏正确的引用,并且printf
是要受到青睐echo
,
$ eval 'printf "%s\n" "${'"$var2"'}"'
Something
然而,eval 会对不受信任的数据提出安全问题。假设
var2="variable;rm importantFile"
。在这种情况下,eval 通过
echo $variable;rm importantFile
到 shell,importantFile
如果存在的话,它会愉快地删除 。
在某些 shell(例如:Bash、ksh、Zsh)中,您还可以使用间接。 Bash 中间接扩展的语法为:
$ echo "${!var2}"
Something
var2="variable;rm importantFile"
不再是问题,但var2='a[$(rm importantFile)]'
仍然是问题。
阅读有关间接的更多信息bash 手册。
如果parameter的第一个字符是感叹号(!),并且parameter不是nameref,则它引入了间接级别。 Bash 将剩余的参数展开后形成的值作为新参数;然后将其扩展,并且该值将用于扩展的其余部分,而不是原始参数的扩展
答案2
闻起来像反模式(*)。许多 shell 都有字符串索引数组/字典。在ksh93
(该语法的来源),bash
或zsh
:
typeset -A dictionary
x="keyX"
y="keyY"
dictionary[keyX]="valueX"
dictionary[$y]="valueY"
printf '%s\n' "${dictionary[$x]}"
printf '%s\n' "${dictionary[keyY]}"
(*) 与 Linux 本身无关。变量中的变量名是一个非常通用的变量“反模式”,一些不应该做的事情,如果你认为你需要它,那么你的设计就有问题(XY问题)?变量中的变量名的大多数用法最好用字典(如果存在)替换。
答案3
你所观察到的是标准行为的一个POSIXshell:一般来说,它
读取其输入;
将输入分解为标记:单词和运算符 (令牌识别);
- 在此步骤中,任何时候遇到未引用的
$
(或`
),它递归地确定扩展类型和要扩展的标记,读取所有需要的输入;
- 在此步骤中,任何时候遇到未引用的
将输入解析为简单命令和复合命令;
执行各种扩展(分别)在每个命令的不同部分,产生被视为命令和参数的路径名和字段列表;
执行重定向并从参数列表中删除重定向运算符及其操作数;
执行函数、内置可执行文件或脚本;
(可选)等待命令完成并收集退出状态。
解析 时echo "\$$(echo $var2)"
,shell 会检测到两个扩展(步骤 2):双引号命令替换$(echo $var2)
和不带引号的参数扩展$var2
。转义的$
in\$
被视为字面上的美元符号,因为双引号\
保留了其作为美元符号的作用转义字符当后面跟着$
.
在后期阶段不会发生进一步的扩展检测。具体来说,没有对步骤 4 ( "\$$(echo $var2)"
→ "\$$(echo variable)"
→ "\$variable"
→ $variable
) 中执行的扩展结果进行进一步的解析来检测扩展触发字符。
另请注意,虽然该$
符号用于在参数扩展上下文中用变量的内容替换变量的名称,但它并未被设计为通用的解引用运算符。
在标准参数扩展,其最简单的形式是${parameter}
, 这parameter
规范只允许是变量名、位置参数或特殊参数(参见“参数”的定义)。严格来说,参数扩展不能嵌套(扩展表达式只允许作为word
在各种${parameter<symbols>[word]}
形式)。
您可以轻松验证这一点,使用Z 壳除外,${${foo}}
不是一个有效的表达式,它$$
扩展为 shell 的 PID(因此,$foo
扩展为 的值foo
,$$foo
扩展为 shell 的 PID 和文字“foo”的串联,$$$foo
扩展为 shell 的 PID 和 的值的串联foo
, ...)。
答案4
除了间接变量扩展之外,在 bash(从版本 4.3 开始)中,您还可以使用名称引用
declare -n var2="variable" # "var2" is a _reference_ to "variable"
variable="Something"
echo "$var2" # => Something
variable="something else"
echo "$var2" # => something else
unset variable
echo "$var2" # => ""
我不知道这是如何实现的,但这是一个有趣的花絮:您可以使用间接找到 nameref 所指的内容:
echo ${!var2} # => variable