不使用 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 的间接寻址的主要内容,如果未能解决你的问题,请参考以下文章

基于转换的索引模式到间接寻址模式(x86 汇编)

在间接寻址中非法使用寄存器

(计算机组成原理)第四章指令系统-第二节2:数据寻址(直接寻址间接寻址立即数)

西门子博图HMI内部变量怎么寻址外部变量

嵌入式:ARM间接寻址变址寻址与多寄存器寻址

间接寻址和数组