How to assign space-containing values to variables in bash using eval
Don't use eval, use declare
$ declare "$var_name=$var_value"
$ echo "fruit: >$fruit<"
fruit: >blue orange<
Don't use eval
for this; use declare
.
var_name="fruit"
var_value="blue orange"
declare "$var_name=$var_value"
Note that word-splitting is not an issue, because everything following the =
is treated as the value by declare
, not just the first word.
In bash
4.3, named references make this a little simpler.
$ declare -n var_name=fruit
$ var_name="blue orange"
$ echo $fruit
blue orange
You can make eval
work, but you still shouldn't :) Using eval
is a bad habit to get into.
$ eval "$(printf "%q=%q" "$var_name" "$var_value")"
A good way to work with eval
is to replace it with echo
for testing. echo
and eval
work the same (if we set aside the \x
expansion done by some echo
implementations like bash
's under some conditions).
Both commands join their arguments with one space in between. The difference is that echo
displays the result while eval
evaluates/interprets as shell code the result.
So, to see what shell code
eval $(echo $var_name=$var_value)
would evaluate, you can run:
$ echo $(echo $var_name=$var_value)
fruit=blue orange
That's not what you want, what you want is:
fruit=$var_value
Also, using $(echo ...)
here doesn't make sense.
To output the above, you'd run:
$ echo "$var_name=\$var_value"
fruit=$var_value
So, to interpret it, that's simply:
eval "$var_name=\$var_value"
Note that it can also be used to set individual array elements:
var_name='myarray[23]'
var_value='something'
eval "$var_name=\$var_value"
As others have said, if you don't care your code being bash
specific, you can use declare
as:
declare "$var_name=$var_value"
However note that it has some side effects.
It limits the scope of the variable to the function where it's run in. So you can't use it for instance in things like:
setvar() {
var_name=$1 var_value=$2
declare "$var_name=$var_value"
}
setvar foo bar
Because that would declare a foo
variable local to setvar
so would be useless.
bash-4.2
added a -g
option for declare
to declare a global variable, but that's not what we want either as our setvar
would set a global var as opposed to that of the caller if the caller was a function, like in:
setvar() {
var_name=$1 var_value=$2
declare -g "$var_name=$var_value"
}
foo() {
local myvar
setvar myvar 'some value'
echo "1: $myvar"
}
foo
echo "2: $myvar"
which would output:
1:
2: some value
Also, note that while declare
is called declare
(actually bash
borrowed the concept from the Korn shell's typeset
builtin), if the variable is already set, declare
doesn't declare a new variable and the way the assignment is done depends on the type of the variable.
For instance:
varname=foo
varvalue='([PATH=1000]=something)'
declare "$varname=$varvalue"
will produce a different result (and potentially have nasty side effects) if varname
was previously declared as a scalar, array or associative array.
Also note that declare
is not any safer than eval
, if the contents of $varname
is not tightly controlled.
For instance, both eval "$varname=\$varvalue"
and declare "$varname=$varvalue"
would reboot the system if $varname
contained a[$(reboot)1]
for instance.