Indirection without using eval

2019-07-20 15:18发布

I'm looking for a clean way (without eval command) to do an indirect reference to an array. Here is a more precise description of what I want :

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?
}

With the following valueof calls :

a=("a" "aa" "aaa")
b=("b" "bb" "bbb")
valueof a
valueof b

The output I have is :

indirection1 $a=a
indirection2 $a=a aa aaa
a
aa
aaa
indirection1 $b=b
indirection2 $b=b bb bbb
b
bb
bbb

On stderr :

prog.sh: line 10: indirection3 $1=${!1[@]}: bad substitution
prog.sh: line 10: indirection3 $1=${!1[@]}: bad substitution

Thanks for your answers/remarks on this question :)

1条回答
做个烂人
2楼-- · 2019-07-20 15:46

I suggest building Bash from the git devel branch and using typeset -n like most other sane shells with arrays do. All other solutions involving functions and arrays require either eval or exploiting quirky undocumented behaviours. Both require about equal care and there isn't necessarily an advantage to one over the other.

Here is a general example which demonstrates just about everything you can do indirectly without eval. Namespace collisions are still possible.

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

This is really eval in disguise in some respects. Most people are unaware that they are effectively performing eval any time they pass variable names to builtins and arithmetic expressions. You have to always ensure that variable names and indexes are controlled internally and never influenced by user input or other side-effects that you can't guarantee the results of.

Judging by your misuse of wordsplitting and quoting, you should probably just switch to a different language. Bash isn't really meant to deal with safe encapsulation.

查看更多
登录 后发表回答