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.
eval
that way is wrong. You're expanding$var_value
before passing it toeval
which means it's going to be interpreted as shell code! (try for instance withvar_value="';:(){ :|:&};:'"
) – Stéphane Chazelas Sep 11 '14 at 21:57eval
(which is one reason I said you shouldn't useeval
). – chepner Sep 11 '14 at 21:59$var_value
is one of quote inversion - assuming a safe value for$var_name
(which can be just as dangerous an assumption, really), then you should be enclosing the right-hand-side's double-quotes within single-quotes - not vice-versa. – mikeserv Sep 12 '14 at 08:51eval
, usingprintf
and itsbash
-specific%q
format. This is still not a recommendation to useeval
, but I think it is safer than it was before. That fact that you need to go to this much effort to get it to work is proof that you should be usingdeclare
or named references instead. – chepner Sep 12 '14 at 12:00set -- a bunch of args; eval "process2 $(process1 "$@")"
whereprocess1
just prints quoted numbers like"${1}" "${8}" "${138}"
. That's crazy simple - and as easy as'"${'$((i=$i+1))'}" '
in most cases. indexed references make it safe, robust, and fast. Still - I upvoted. – mikeserv Sep 12 '14 at 13:06%q
. I always feed dirty for receiving rep for "don't do this" answers. – glenn jackman Sep 12 '14 at 13:09