不使用 eval 的间接寻址
Posted
技术标签:
【中文标题】不使用 eval 的间接寻址【英文标题】:Indirection without using eval 【发布时间】:2012-11-12 13:11:19 【问题描述】:我正在寻找一种简洁的方式(没有 eval 命令)来间接引用数组。这是我想要的更准确的描述:
function valueof
echo "indirection1 \$$1=$!1"
eval "echo indirection2 \\\$$1=\$$1[@]" # Untill this step its fine.
# My final objective is to do this (but eval is not a very sexy solution) :
for i in $(eval "echo \$$1[@]") ; do
echo $i
done
# Here is the "problem", ie. "bad substitution"
echo "indirection3 \$$1=$!1[@]"
# "1[@]" is evaluated first i guess?
使用以下值调用:
a=("a" "aa" "aaa")
b=("b" "bb" "bbb")
valueof a
valueof b
我的输出是:
indirection1 $a=a
indirection2 $a=a aa aaa
a
aa
aaa
indirection1 $b=b
indirection2 $b=b bb bbb
b
bb
bbb
在标准错误上:
prog.sh: line 10: indirection3 $1=$!1[@]: bad substitution
prog.sh: line 10: indirection3 $1=$!1[@]: bad substitution
感谢您对此问题的回答/评论:)
【问题讨论】:
顺便说一句,您的错误是 ksh 不幸的设计决定的结果,它使用前缀和后缀作为扩展运算符,而 bash 继承了这一点,并创建了自己对!
前缀的冲突使用用于间接(与其他 shell 中的间接相关的另一个冲突使用)。 "$!var[@]"
表示扩展 var 的键列表,优先级高于 $!var
。
好的,谢谢你的精确。这就是为什么在您的代码中我们可以看到类似'"$1"'
的内容,以便首先扩展该部分?
部分,但它也利用了一些血淋淋的细节,而不是在 Bash 中有条件地解析“声明命令”的参数的方式,以便在某些条件下获得类似 eval 的双重扩展(它不是一个错误)。正在使用的其他类型的间接是隐式算术变量间接,并利用下标语法被间接视为参数名称的一部分。这些都不是真正“支持”的,我花了太多时间来发现它,以便编写更好的可重用库(没人能破译。)
【参考方案1】:
我建议从 git devel 分支构建 Bash 并使用 typeset -n
,就像大多数其他带有数组的健全的 shell 一样。所有其他涉及函数和数组的解决方案都需要eval
或利用古怪的未记录行为。两者都需要同等的照顾,并且彼此之间不一定有优势。
这是一个通用示例,它演示了您可以在不使用 eval 的情况下间接执行的所有操作。命名空间冲突仍然可能发生。
isSubset()
local -a 'xkeys=("$!'"$1"'[@]")' 'ykeys=("$!'"$2"'[@]")'
set -- "$@/%/[key]"
(( $#xkeys[@] <= $#ykeys[@] )) || return 1
local key
for key in "$xkeys[@]"; do
[[ $!2+_ && $!1 == "$!2" ]] || return 1
done
a=(abc def [4]=ghi jkl)
b=(abc def [4]=ghi jkl)
c=(abc [3]=def [6]=ghi xyz)
isSubset a b
echo $? # 0
isSubset b c
echo $? # 1
这确实是eval
在某些方面的伪装。大多数人都没有意识到,每当他们将变量名传递给内置函数和算术表达式时,他们都在有效地执行 eval。您必须始终确保变量名称和索引在内部受到控制,并且不会受到用户输入或您无法保证结果的其他副作用的影响。
根据您对分词和引用的滥用判断,您可能应该改用另一种语言。 Bash 并不是真的要处理安全封装。
【讨论】:
以上是关于不使用 eval 的间接寻址的主要内容,如果未能解决你的问题,请参考以下文章