Search code examples
bashevalvariable-assignmentquotes

Indirect variable assignment in bash


Seems that the recommended way of doing indirect variable setting in bash is to use eval:

var=x; val=foo
eval $var=$val
echo $x  # --> foo

The problem is the usual one with eval:

var=x; val=1$'\n'pwd
eval $var=$val  # bad output here

(and since it is recommended in many places, I wonder just how many scripts are vulnerable because of this...)

In any case, the obvious solution of using (escaped) quotes doesn't really work:

var=x; val=1\"$'\n'pwd\"
eval $var=\"$val\"  # fail with the above

The thing is that bash has indirect variable reference baked in (with ${!foo}), but I don't see any such way to do indirect assignment -- is there any sane way to do this?

For the record, I did find a solution, but this is not something that I'd consider "sane"...:

eval "$var='"${val//\'/\'\"\'\"\'}"'"

Solution

  • The main point is that the recommended way to do this is:

    eval "$var=\$val"
    

    with the RHS done indirectly too. Since eval is used in the same environment, it will have $val bound, so deferring it works, and since now it's just a variable. Since the $val variable has a known name, there are no issues with quoting, and it could have even been written as:

    eval $var=\$val
    

    But since it's better to always add quotes, the former is better, or even this:

    eval "$var=\"\$val\""
    

    A better alternative in bash that was mentioned for the whole thing that avoids eval completely (and is not as subtle as declare etc):

    printf -v "$var" "%s" "$val"
    

    Though this is not a direct answer what I originally asked...