¿Es posible imprimir el contenido del contenido de una variable con shell script? (referencia indirecta)

13

Supongamos que he declarado las siguientes variables:

$ var='$test'
$ test="my string"

Si imprimo su contenido, veo lo siguiente:

$ echo $var
$test

$ echo $test
my string

Me gustaría encontrar una manera de imprimir el contenido del contenido de $var(que es el contenido de $test). Entonces intenté hacer lo siguiente:

$ echo $(echo $var)
$test

Pero aquí el resultado es $testy no "my string"... ¿Es posible imprimir el contenido del contenido de variables usando bash?

Rafael Muynarsk
fuente
¿Podrías editar tu publicación para decir qué shell estás usando o qué requisitos de portabilidad tienes?
Michael Homer
@MichaelHomer Claro, pero ¿cómo puedo verificar exactamente esta información?
Rafael Muynarsk
Es más algo que eliges que algo para verificar. Por ejemplo, ¿está ejecutando sus scripts con ash, o bash, o csh, o dash, ..., o zsh, o POSIX sh, o necesita trabajar en diferentes sistemas?
Michael Homer
@MichaelHomer Ah, ya veo ... estoy usando bash. Simplemente cambiaré la pregunta final e insertaré una nueva etiqueta.
Rafael Muynarsk

Respuestas:

21

Puede lograr esto usando la expansión de variables indirectas de bash (siempre que esté bien que omita el $de su variable de referencia):

$ var=test
$ test="my string"
$ echo "$var"
test
$ echo "${!var}"
my string

3.5.3 Expansión del parámetro de la carcasa

jesse_b
fuente
10

Para el caso en que el nombre de la variable contenida en el varprefijo $puede usar eval:

$ var='$test'
$ test="my string"
$ eval echo $var
my string

Qué pasa aquí:

  • bash se expande $varal valor $test, produciendo un eval echo $testcomando;
  • evalevalúa la echo $testexpresión y produce un resultado deseado.

Tenga en cuenta que el uso evalen general puede ser peligroso (dependiendo de lo que esté almacenado var), por lo que prefiere evitarlo. La función de expansión indirecta es mejor para su caso (pero debe deshacerse del $inicio de sesión $test).

Danila Kiver
fuente
Claro, eso es un tipo incorrecto. La publicación original tiene comillas simples. Fijo.
Danila Kiver
44
Siempre recomiendo referencias variables de comillas dobles (para evitar problemas por la división inesperada de palabras y la expansión de comodines). Con eval, se necesitan dos capas de comillas dobles, la línea siguiente: eval echo "\"$var\"". Eso sí, esto no hace nada sobre los otros peligros de usar evalque mencionaste.
Gordon Davisson
9

Similar a la respuesta de Jesse_b , pero usando una variable de referencia de nombre en lugar de indirecta variable (requiere bash4.3+):

$ declare -n var=test
$ test="my string"
$ echo "$var"
my string

La variable de referencia de nombre varcontiene el nombre de la variable a la que hace referencia. Cuando la variable se desreferencia como $var, se devuelve el valor de esa otra variable.

bash resuelve referencias de nombre de forma recursiva:

$ declare -n var1=var2
$ declare -n var2=test
$ test="hello world"
$ echo "$var1"
hello world

Para completar, usar una matriz asociativa (en bash4.0+) también es una forma de resolver esto, dependiendo de los requisitos:

$ declare -A strings
$ strings[test]="my string"
$ var=test
$ echo "${strings[$var]}"
my string

Esto proporciona una forma más flexible de acceder a más de un valor por una clave o nombre que puede determinarse dinámicamente. Esto puede ser preferible si desea recopilar todos los valores de una categoría particular en una sola matriz, pero aún así puede acceder a ellos mediante alguna clave (por ejemplo, nombres accesibles por ID o nombres de ruta accesibles por propósito, etc.) ya que no contamina El espacio de nombres variable del script.

Kusalananda
fuente
3

Con zsh:

$ var='$test'
$ test='*'
$ printf '%s\n' ${(e)var}
*

Con cualquier caparazón tipo Bourne

$ eval 'printf "%s\n" "'"$var"'"'
*

Recuerde que en esos shells se deben citar las expansiones variables para evitar split + glob (que zshes una excepción) y echono se pueden usar para datos arbitrarios.

Dado que con ambos (e)y evalhay una evaluación del código de shell, es importante que el contenido $varpermanezca bajo su control, ya que de lo contrario puede ser una vulnerabilidad de inyección de comando arbitraria (lo mismo con los ${!var}enfoques).

Stéphane Chazelas
fuente