214

How can I retrieve a bash variable value if I have the variable name as string?

var1="this is the real value"
a="var1"
Do something to get value of var1 just using variable a.

Context:

I have some AMI's (Amazon Machine Image) and I want to fire up a few instances of each AMI. As soon as they finish booting, I want to setup each instance according to its AMI type. I don't want to bake lots of scripts or secret keys inside any AMI so I prepared a generalized startup script and I put it on S3 with a publicly accessible link. In rc.local I put small piece of code which fetches the startup script and executes it. This is all I have in the AMIs. Then each AMI accesses a common configuration script which is applicable to all AMIs and special setup scripts for each. These scripts are private and require a signed URL to access them.

So now, when I fire an instance of an AMI (my_private_ami_1), I pass a signed URL for one more file presented on S3 which contains signed URL for all private scripts in terms of key/value pair.

config_url="http://s3.amazo.../config?signature"
my_private_ami_1="http://s3.amazo.../ami_1?signature"
...
When the startup script runs, it downloads the above file and source's it. Then it checks for its AMI type and picks the correct setup script for itself.
ami\_type=GET AMI TYPE #ex: sets ami\_type to my\_private\_ami\_1
setup\_url=GET THE SETUP FILE URL BASED ON AMI\_TYPE # this is where this problem arises

So now I can have a generic code which can fire instances irrespective of their AMI types and instances can take care of themselves.

Community
  • 1
  • 1
bhups
  • 14,345
  • 8
  • 49
  • 57

11 Answers11

397

You can use ${!a}:

var1="this is the real value"
a="var1"
echo "${!a}" # outputs 'this is the real value'

This is an example of indirect parameter expansion:

The basic form of parameter expansion is ${parameter}. The value of parameter is substituted.

If the first character of parameter is an exclamation point (!), it introduces a level of variable indirection. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself.

Phil Ross
  • 25,590
  • 9
  • 67
  • 77
  • 4
    This works for me in OSX bash, but not debian? On debian I get `Bad substitution` error. – 23inhouse Aug 08 '13 at 03:16
  • 16
    @23inhouse Are you running your script using `/bin/sh`? If so, try using `/bin/bash` instead. From Debian Squeeze onwards, `/bin/sh` was changed to be a [symlink for `dash` instead of `bash`](https://wiki.debian.org/Shell#Default_.2Fbin.2Fsh_shell). `dash` doesn't support this particular syntax and will output a `Bad substitution` error. – Phil Ross Aug 08 '13 at 13:56
  • 10
    Is there a way to make this work for a variable name that belongs to an array? – Loupax Jan 15 '14 at 12:05
  • Works on ubuntu with bash – Sergey P. aka azure Sep 25 '14 at 12:13
  • what is he name of this operation? (want to read about it further more) – Doron Shai May 28 '15 at 11:32
  • 1
    @DoronShai This is an example of indirect parameter expansion, documented in the Bash Reference Manual at http://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html – Phil Ross May 28 '15 at 11:52
  • I also get `syntax error: bad substitution` with ash, is there an ash equivalent of that? – Maxim Aug 21 '19 at 17:57
  • @Maxim according to [this answer](https://stackoverflow.com/a/1014604/5981) you can use `eval "echo \$$a"` with ash. – Phil Ross Aug 21 '19 at 19:32
  • Wow! I almost thought this is not possible. How do you guys know this stuff? – FractalSpace Aug 28 '19 at 21:02
  • This tripped me up for a bit so I thought I'd share. If you do this, even in bash, all in one line such as `a="hi" b="a" echo "${!b}"` then it won't work. If you break out the declarations of a and be on to separate lines, each as a single command then it does work. It seems like the variable expansion requires the variable be declared in a higher scope or something. – justin.m.chase Mar 07 '20 at 04:46
  • @justin.m.chase you need a semicolon after each one – smac89 Jul 11 '20 at 02:32
  • Oh, neat, this even works in conjunction with other variable expansions like `${!a+x}` (even though it looks so wrong). – Caesar Oct 23 '20 at 06:28
41
X=foo
Y=X
eval "Z=\$$Y"

sets Z to foo.

oguz ismail
  • 1
  • 16
  • 47
  • 69
23

For my fellow zsh users, the way to accomplish the same thing as the accepted answer is to use:

echo ${(P)a} # outputs 'this is the real value'

It is appropriately called Parameter name replacement

This forces the value of the parameter name to be interpreted as a further parameter name, whose value will be used where appropriate. Note that flags set with one of the typeset family of commands (in particular case transformations) are not applied to the value of name used in this fashion.

If used with a nested parameter or command substitution, the result of that will be taken as a parameter name in the same way. For example, if you have ‘foo=bar’ and ‘bar=baz’, the strings ${(P)foo}, ${(P)${foo}}, and ${(P)$(echo bar)} will be expanded to ‘baz’.

Likewise, if the reference is itself nested, the expression with the flag is treated as if it were directly replaced by the parameter name. It is an error if this nested substitution produces an array with more than one word. For example, if ‘name=assoc’ where the parameter assoc is an associative array, then ‘${${(P)name}[elt]}’ refers to the element of the associative subscripted ‘elt’.

smac89
  • 39,374
  • 15
  • 132
  • 179
15

In bash 4.3+, you can use declare -n:

#!/usr/bin/env bash

var="this is the real value"
var_name="var"

declare -n var_ref=$var_name
echo "${var_ref}"
Mr Z
  • 159
  • 2
  • 3
13

Modified my search keywords and Got it :).

eval a=\$$a
Thanks for your time.
bhups
  • 14,345
  • 8
  • 49
  • 57
9

Had the same issue with arrays, here is how to do it if you're manipulating arrays too :

array_name="ARRAY_NAME"
ARRAY_NAME=("Val0" "Val1" "Val2")

ARRAY=$array_name[@]
echo "ARRAY=${ARRAY}"
ARRAY=("${!ARRAY}")
echo "ARRAY=${ARRAY[@]}"
echo "ARRAY[0]=${ARRAY[0]}"
echo "ARRAY[1]=${ARRAY[1]}"
echo "ARRAY[2]=${ARRAY[2]}"

This will output :

ARRAY=ARRAY_NAME[@]
ARRAY=Val0 Val1 Val2
ARRAY[0]=Val0
ARRAY[1]=Val1
ARRAY[2]=Val2
Alexandre Hamon
  • 1,162
  • 12
  • 13
  • 1
    +1 for this is the only correct answer for array, to make it clearer, the string must have `[@]` suffix to obtain the full array. – Reorx Feb 07 '22 at 10:26
2

In bash 4.3, the '-v' test for set variables was introduced. At the same time, 'nameref' declaration was added. These two features together with the indirection operator (!) enable a simplified version of the previous example:

get_value()
{
  declare -n var_name=$1
  if [[ -v var_name ]]
  then
    echo "${var_name}"
  else
    echo "variable with name <${!var_name}> is not set"
  fi
}

test=123
get_value test
123

test="\$(echo \"something nasty\")"
get_value test
$(echo "something nasty")

unset test
get_value test
variable with name <test> is not set

As this approach eliminates the need for 'eval', it is safer. This code checked under bash 5.0.3(1).

user8150417
  • 61
  • 1
  • 6
1

modern shells already support arrays( and even associative arrays). So please do use them, and use less of eval.

var1="this is the real value"
array=("$var1")
# or array[0]="$var1"

then when you want to call it , echo ${array[0]}

ghostdog74
  • 327,991
  • 56
  • 259
  • 343
1

Extending the answer of @MrZ:

Using declare -n you can generate a reference to any variable whose name is generated dynamically. For example:

$ var_one='This is variable 1'
$ ind=one
$ declare -n var_ref=var_$ind
$ echo $var_ref
This is variable 1
$ ind=two
$ echo $var_ref
This is variable 1
$ declare -n var_ref=var_$ind
$ echo $var_ref

$ var_two="And this is the second variable"
$ echo $var_ref
And this is the second variable
FedKad
  • 493
  • 4
  • 19
0

Based on the answer: https://unix.stackexchange.com/a/111627

###############################################################################
# Summary: Returns the value of a variable given it's name as a string.
# Required Positional Argument: 
#   variable_name - The name of the variable to return the value of
# Returns: The value if variable exists; otherwise, empty string ("").
###############################################################################
get_value_of()
{
    variable_name=$1
    variable_value=""
    if set | grep -q "^$variable_name="; then
        eval variable_value="\$$variable_name"
    fi
    echo "$variable_value"
}

test=123
get_value_of test
# 123
test="\$(echo \"something nasty\")"
get_value_of test
# $(echo "something nasty")
VFein
  • 1,021
  • 2
  • 11
  • 23
-1
VALUE=$(eval "echo \$$SOME_VAR_NAME")

Example

SSH_KEY_FILE_PATH_FOO="/tmp/key"
SSH_KEY_FILE_PATH_VAR_NAME_PREFIX="SSH_KEY_FILE_PATH"
SSH_KEY_FILE_PATH_VAR_NAME_SUFFIX="FOO"
SSH_KEY_FILE_PATH=$(eval "echo \$${SSH_KEY_FILE_PATH_VAR_NAME_PREFIX}_${SSH_KEY_FILE_PATH_VAR_NAME_SUFFIX}")
echo "$SSH_KEY_FILE_PATH"
/tmp/key
MingalevME
  • 1,827
  • 1
  • 22
  • 19