什么';这些Bash间接寻址方法之间的区别是什么?

什么';这些Bash间接寻址方法之间的区别是什么?,bash,shell,Bash,Shell,我在Bash(macOS上的4.4.19通过MacPorts)中探索间接变量访问的不同方法时,偶然发现了这种不一致性: 为什么下面的第二个循环在IFS设置为“:”时只循环一次 注意:IFS的影响在这里是显著的 #!/usr/bin/env bash PATH="foo:bar:baz" var=PATH IFS=':' echo ${!var} # returns foo bar baz echo $(eval echo \$$var) # also returns foo bar baz

我在Bash(macOS上的4.4.19通过MacPorts)中探索间接变量访问的不同方法时,偶然发现了这种不一致性:

为什么下面的第二个循环在IFS设置为“:”时只循环一次

注意:IFS的影响在这里是显著的

#!/usr/bin/env bash
PATH="foo:bar:baz"
var=PATH
IFS=':'

echo ${!var}
# returns foo bar baz
echo $(eval echo \$$var)
# also returns foo bar baz

for V in ${!var}; do
    echo $V
done
# returns foo\nbar\nbaz\n

for V in $(eval echo \$$var); do
    echo $V
done
# returns foo bar baz
如果用空格分隔的项目和删除的IFS替换
路径
内容,则在这两种情况下都能正常工作。

特定于代码的演示 …输出:

示例1:Eval运行无引号的Echo
-福吧巴兹酒店
示例2:Eval运行引用的Echo
-福
-酒吧
-巴兹
示例3:带有无引号Echo的间接扩展语法
-福吧巴兹酒店
示例4:不带无引号扩展的间接扩展语法
-福
-酒吧
-巴兹
工作示例和中断示例之间的差异与是否使用
eval
无关;它只与是否存在不带引号的
echo
子流程有关


广义答案 解释差异:无引号的命令替换 给出的案例之间的输出差异与间接寻址无关,而与无引号的扩展有关;看

考虑以下示例,它根本不使用间接寻址:

var='foo
bar
baz'

echo "Correct version:"
echo "$var"
echo
echo "Incorrect version:"
echo $var
……其产出是:

正确版本:
福
酒吧
巴兹
不正确的版本:
福吧巴兹酒店
这里根本没有间接性——唯一的区别是
echo$foo
echo“$foo”
之间的区别

以完全相同的方式,
echo$(…)
删除新行,而
echo“$(…)”
将保留它们。


那么,为什么会有多种命令替换机制呢? 因为使用
eval
的是不安全的。真的,危险的是,永远不要用这个不安全的

考虑:

## THIS IS INSECURE; NEVER DO THIS
varname='foo$(touch /tmp/evil)'
foo="Value"
eval "echo \"\$$varname\""
运行时,它会回显
——但也会创建
/tmp/evil
。它可以代替运行任何其他攻击者选择的命令

与之相比:

varname='foo$(touch /tmp/evil)'
foo="Value"
echo "${!foo}"

…它实际上不运行命令替换。在几乎任何可以利用shellshock的情况下(即攻击者可以设置任意环境变量值),使用
eval
方法都会破坏您的安全性;使用间接方法是安全的(r)。

如果输入无效,这些情况中的一些可能存在安全漏洞。当
var='PATH$(touch/tmp/evil)
时,有些版本(特别是基于
eval
的版本)将创建
/tmp/evil
(攻击者可以不删除您的主目录或执行任何其他能够设置
var
的操作)。其他人会抛出错误。在这种情况下,哪一个更安全是很清楚的。顺便说一句,这里有很多基于引用的错误。通过运行代码可以找到它们;另请参见……相比之下,在${!var}中的
for V中,没有
echo
转换为空格,因此当它们将字符串拆分为多个片段以供
for循环迭代时,它们仍然是冒号,因此循环会执行多次。但是如果您将
for V in$(eval echo\$$var)更改为
;对V在$(eval“echo\”\$$var\)中执行
;如果执行
,则该行为与其他表单相同,但存在额外的安全漏洞。你在这里问的纯粹是引用的差异,没有别的。谢谢你详细的解释和警告,但可能因为时间晚了,我不太明白这和我的问题有什么关系。我的主要困惑是,在我的示例中,最初的两个“echo”返回相同的东西,因此我认为这两个间接方法的输出是相同的。很明显,有一些扩展,我没有看到,影响了它在循环中的行为。你能澄清一下吗?试试这个:
echo$(echo“first line”;echo“second line”)
,并将其与
echo“$(echo“first line”;echo“second line”)”
进行比较——这能说明问题吗?缺少引号是导致命令替换的输出被字分割并作为一系列单独的参数传递给
echo
,然后
echo
与空格连接(忽略原始换行符)…因此,两种间接方法都正确地尊重换行符,但是错误的引语却把这些新词扔掉了。
varname='foo$(touch /tmp/evil)'
foo="Value"
echo "${!foo}"