• Uncategorized

About string : How-to-get-a-variable-value-if-variable-name-is-stored-as-string

Question Detail

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.

Question Answer

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.

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

sets Z to foo.

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

Modified my search keywords and Got it :).

eval a=\$$a

Thanks for your time.

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

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

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

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

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

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

You may also like...

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.