How to get a variable value if variable name is stored as string?

StringBash

String Problem Overview


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.

String Solutions


Solution 1 - String

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.

Solution 2 - String

X=foo
Y=X
eval "Z=\$$Y"

sets Z to "foo"

Take care using eval since this may allow accidential excution of code through values in ${Y}. This may cause harm through code injection.

For example

Y="\`touch /tmp/eval-is-evil\`"

would create /tmp/eval-is-evil. This could also be some rm -rf /, of course.

Solution 3 - String

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’.

Solution 4 - String

Modified my search keywords and Got it :).

eval a=$$a
Thanks for your time.

Solution 5 - String

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}"

Solution 6 - String

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

Solution 7 - String

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).

Solution 8 - String

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]}

Solution 9 - String

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")

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionbhupsView Question on Stackoverflow
Solution 1 - StringPhil RossView Answer on Stackoverflow
Solution 2 - StringanonView Answer on Stackoverflow
Solution 3 - Stringsmac89View Answer on Stackoverflow
Solution 4 - StringbhupsView Answer on Stackoverflow
Solution 5 - StringMr ZView Answer on Stackoverflow
Solution 6 - StringAlexandre HamonView Answer on Stackoverflow
Solution 7 - Stringuser8150417View Answer on Stackoverflow
Solution 8 - Stringghostdog74View Answer on Stackoverflow
Solution 9 - StringVFeinView Answer on Stackoverflow