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.