遇到了这个明显的异常现象。测试代码:
(
set -x
declare -A a=()
for i in x "'" '"'
do
a[$i]=
test -v a[$i] && true
done
: "${!a[@]}"
)
输出:
+ a=()
+ declare -A a
+ for i in x "'" '"'
+ a[$i]=
+ test -v 'a[x]'
+ true
+ for i in x "'" '"'
+ a[$i]=
+ test -v 'a['\'']'
+ for i in x "'" '"'
+ a[$i]=
+ test -v 'a["]'
+ : \' '"' x
我预计
true
会被执行三次,而不仅仅是一次。似乎 test -v
对于关联数组元素失败,其中索引扩展为包含引号字符的内容。
这是一个 bash 错误,还是只是我需要更明智的期望?
似乎是一个错误。
不过我找到了解决方法:
declare -A a=()
for i in x '"a"' a '"a"' ']' '[' '\' "'" '"' y
do
a[$i]=
[[ -v 'a[$i]' ]] && echo "true $i"
done
好的,猜对了。
这有效:
(
set -x
declare -A a=()
a[x]= a["'"]= a['"']=
for i in x "'" '"' y
do
test -v 'a[$i]' && true
done
: "${!a[@]}"
)
输出:
+ a=()
+ declare -A a
+ a[x]=
+ a["'"]=
+ a['"']=
+ for i in x "'" '"' y
+ test -v 'a[$i]'
+ true
+ for i in x "'" '"' y
+ test -v 'a[$i]'
+ true
+ for i in x "'" '"' y
+ test -v 'a[$i]'
+ true
+ for i in x "'" '"' y
+ test -v 'a[$i]'
+ : \' '"' x
现在,唯一失败的
test -v
是数组元素 a[y]
的那个,它确实从未被设置过。
不成文的规则似乎是,提供给
name
的 test -v name
参数的处理方式与 shell 解析命令时 shell 变量赋值中 =
左侧的任何内容完全相同线。具体来说,在测试结果名称是否具有所指对象之前,将评估任何此类 name
的下标部分内的任何扩展语法。此外,为了使报价删除正确运行,此类扩展必须以这种方式推迟;在 name
作为参数传递给 test
之前,不能允许它们发生。
同样适用于作为参数提供给
unset
的名称。
我在 bash 手册中能找到的最接近这一点的是数组部分中的这一段:
当使用带下标的变量名作为变量的参数时 命令,例如 unset,不使用单词扩展语法 如上所述,参数取决于 shell 的文件名 扩张。如果不需要扩展文件名,则该参数应该 被引用。
我觉得这与当前的问题有点无关。我不认为这只是变量扩展:我认为它是完整的 Monty。如果您感觉有点颤抖,请尝试
unset 'a[x$(ls -al >&2)]'
;那里可能潜伏着一类有趣的脚本注入漏洞。
总结:如果您要使用
test -v
来测试关联数组中是否存在某个条目,请将名称用单引号而不是双引号括起来,即使它包含其他变量的扩展也是如此。你想要 test -v 'a[$i]'
,而不是任何像 test -v "a[$i]"
一样扩展的东西。同样适用于unset
。