How to get a variable value if variable name is stored as string?
StringBashString 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")