11

E.g. next script will print Hello due to ${! rule.

A=B
B=Hello
echo ${!A}

But how can I set value to variable with indirect name? Straightforward use of ! sign doesn't work:

A=B
!A=Hello # here is tricky line
echo $B

I know there is a trick with using temp file, but I'm interested in using indirection, not something like

A=B
echo "$A=Hello" > 1.tmp
. 1.tmp
echo $B

5 Answers5

16

You can do this with the typeset builtin:

$ A=B
$ typeset $A=42
$ echo $B
42

Or using declare:

$ declare $A=1
$ echo $B
1
Mat
  • 52,586
9

Following on indirection when getting the value of a variable

The portable way is to use eval. You do have to pay attention to the quoting so that special characters in the value are not evaluated when they shouldn't be. The easiest way is to store the new value in an intermediate variable and assign the value of that variable.

A=B
tmp='stuff with special characters or whatever…'
eval $A=\$tmp

The argument to eval is B=$tmp, a straightforward assignment. You don't need double quotes around $tmp even if it contains whitespace or globbing characters, because those are not expanded in an assignment.

If you want to make an environment variable, you can use export. In bash or ksh or zsh, you can use typeset to make a local variable (declare is synonymous in bash). Again, you should use an intermediate variable so that special characters are not mangled. Note that except in zsh, you need to put double quotes around the variable expansion, because this is not an assignment but a builtin that takes an argument that happens to look like an assignment.

export $A="$tmp"
  • This (using eval) is the correct answer. It's portable, adheres to the POSIX standard, and is the one used all over the place in more advanced shell scripts. One thing, though: using export on the same line as the assignment is not portable. – MadScientist Mar 19 '13 at 12:32
  • @MadScientist export var=value is POSIX. It isn't portable to Bourne shells, but these are a vanishing breed (just make sure to use /usr/xpg4/bin/sh or /usr/xpg6/bin/sh or ksh and not /bin/sh on older Solaris machines). – Gilles 'SO- stop being evil' Mar 19 '13 at 12:58
5

Another way you could try:

$ A=B
$ read $A <<< 81
$ echo "$B"
81

But there is a security risk (!) as with all these methods (also declare / typeset and of course eval) .. In this case one must control the lefthand side (the value of $A), in other words, at least in bash, variable $A should not contain user controlled input, for example an input file, etcetera...


If your shell does not support a here-string ( <<< ) you could also use a here-document instead:

A=B
read $A << EOF
82
EOF
echo "$B"
Scrutinizer
  • 1,142
  • 5
  • 7
2

In bash version 5 I use declare -n varref=varname. Then variable could be set as varref=123 and read as echo $varref (does the same as echo $varname) without any additional commands and syntax.

from help declare:

-n make NAME a reference to the variable named by its value

kyb
  • 420
1

Another way to do this is with printf, which assigns its output to a variable whose name may be passed with the -v parameter:

A=B
printf -v "$A" '%s' "Hello"
echo ${!A}