¿Cómo obtener un valor variable si el nombre de la variable se almacena como una cadena?

142

¿Cómo puedo recuperar un valor de variable bash si tengo el nombre de la variable como cadena?

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

Contexto:

Tengo algunos AMI ( Amazon Machine Image ) y quiero encender algunas instancias de cada AMI. Tan pronto como terminen de arrancar, quiero configurar cada instancia de acuerdo con su tipo de AMI. No quiero hornear muchos scripts o claves secretas dentro de cualquier AMI, así que preparé un script de inicio generalizado y lo puse en S3 con un enlace de acceso público. En rc.local pongo un pequeño fragmento de código que recupera el script de inicio y lo ejecuta. Esto es todo lo que tengo en las AMI. Luego, cada AMI accede a un script de configuración común que se aplica a todos los AMI y scripts de configuración especiales para cada uno. Estos scripts son privados y requieren una URL firmada para acceder a ellos.

Entonces, cuando disparo una instancia de un AMI (my_private_ami_1), paso una URL firmada para un archivo más presentado en S3 que contiene una URL firmada para todos los scripts privados en términos de par clave / valor.

config_url="http://s3.amazo.../config?signature"
my_private_ami_1="http://s3.amazo.../ami_1?signature"
...
Cuando se ejecuta el script de inicio, descarga el archivo anterior y sourcelisto. Luego comprueba su tipo de AMI y elige el script de configuración correcto para sí mismo.

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

Así que ahora puedo tener un código genérico que puede disparar instancias independientemente de sus tipos de AMI y las instancias pueden cuidarse por sí mismas.

bhups
fuente

Respuestas:

257

Puedes usar ${!a}:

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

Este es un ejemplo de expansión indirecta de parámetros :

La forma básica de expansión de parámetros es ${parameter}. El valor de parameteres sustituido.

Si el primer carácter de parameteres un signo de exclamación (!), Introduce un nivel de indirección variable. Bash usa el valor de la variable formada a partir del resto de parametercomo el nombre de la variable; esta variable se expande y ese valor se usa en el resto de la sustitución, en lugar del valor de parametersí mismo.

Phil Ross
fuente
44
¿Esto funciona para mí en OSX bash, pero no debian? En debian me sale un Bad substitutionerror.
23inhouse
11
@ 23inhouse ¿Está ejecutando su script usando /bin/sh? Si es así, intente usar en su /bin/bashlugar. De Debian Squeeze en adelante, /bin/shse cambió para ser un enlace simbólico en dashlugar debash . dashno admite esta sintaxis particular y generará un Bad substitutionerror.
Phil Ross
8
¿Hay alguna manera de hacer que esto funcione para un nombre de variable que pertenece a una matriz?
Loupax
Funciona en ubuntu con bash
Sergey P. aka azure
1
@DoronShai Este es un ejemplo de expansión indirecta de parámetros, documentado en el Manual de referencia de Bash en gnu.org/software/bash/manual/html_node/…
Phil Ross
29
X=foo
Y=X
eval "Z=\$$Y"

establece Z en "foo"

Tenga cuidado al usarlo, evalya que esto puede permitir la extirpación accidental del código a través de valores en ${Y}. Esto puede causar daño a través de la inyección de código.

Por ejemplo

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

crearía /tmp/eval-is-evil. Esto también podría ser algo rm -rf /, por supuesto.

tratar de atrapar finalmente
fuente
salvó mi cola, ty
raffian
9

Modifiqué mis palabras clave de búsqueda y lo conseguí :).

eval a=\$$a
Gracias por tu tiempo.

bhups
fuente
Tenga cuidado al usar eval, ya que esto puede permitir la escisión del código a través de valores en ${Y}. Vea mi adición en la respuesta del usuario "anon".
try-catch-finally
Esta es la única respuesta que funciona. ¡Irónico que sea tuyo!
Ctrl S
8

Para mis compañeros usuarios de zsh, la forma de lograr lo mismo que la respuesta aceptada es usar:

${(P)a}

Se llama reemplazo de nombre de parámetro

Esto obliga a que el valor del nombre del parámetro se interprete como un nombre de parámetro adicional, cuyo valor se utilizará cuando corresponda. Tenga en cuenta que los indicadores configurados con una de las familias de comandos tipográficos (en particular las transformaciones de casos) no se aplican al valor del nombre utilizado de esta manera.

Si se usa con un parámetro anidado o una sustitución de comando, el resultado se tomará como un nombre de parámetro de la misma manera. Por ejemplo, si tiene 'foo = bar' y 'bar = baz', las cadenas $ {(P) foo}, $ {(P) $ {foo}} y $ {(P) $ (barra de eco) } se expandirá a 'baz'.

Del mismo modo, si la referencia está anidada, la expresión con el indicador se trata como si hubiera sido reemplazada directamente por el nombre del parámetro. Es un error si esta sustitución anidada produce una matriz con más de una palabra. Por ejemplo, si 'name = assoc' donde el parámetro assoc es una matriz asociativa, entonces '$ {$ {(P) name} [elt]}' se refiere al elemento del 'elt' con subíndice asociativo.

smac89
fuente
0

los shells modernos ya admiten matrices (e incluso matrices asociativas). Así que por favor úselos y use menos de eval.

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

entonces cuando quieras llamarlo, echo $ {array [0]}

ghostdog74
fuente
¿funcionaría esto si obtengo la cadena del argumento de la línea de comandos (por ejemplo, como $1)?
jena
0

Basado en la respuesta: 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
fuente
0

Tuve el mismo problema con las matrices, aquí está cómo hacerlo si también está manipulando matrices:

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

Esto generará:

ARRAY=ARRAY_NAME[@]
ARRAY=Val0 Val1 Val2
ARRAY[0]=Val0
ARRAY[1]=Val1
ARRAY[2]=Val2
Alexandre Hamon
fuente