bash nombres de variables dinámicas (variables)

12

Quiero crear dinámicamente una secuencia de cadenas mediante la manipulación de una matriz de elementos y crear algún procedimiento aritmético.

for name in FIRST SECOND THIRD FOURTH FIFTH; do
    $name = $(( $6 + 1 ))
    $name = "${$name}q;d"
    echo "${$name}"; printf "\n"
done

El resultado deseado sería el siguiente para $6iguales 0.

1q;d
2q;d
3q;d
4q;d
5q;d

Pero me sale este error

reel_first_part.sh: line 18: FIRST: command not found
reel_first_part.sh: line 19: ${$name}q;d: bad substitution
reel_first_part.sh: line 18: FIRST: command not found
reel_first_part.sh: line 19: ${$name}q;d: bad substitution
reel_first_part.sh: line 18: FIRST: command not found
reel_first_part.sh: line 19: ${$name}q;d: bad substitution

Supongo que es algo simple. Solía ​​funcionar cuando hacía algo como

FIRST=$(( $6 + 1 ))
FIRST="${FIRST}q;d"
giannis christofakis
fuente
1
¿Puedes explicarlo un poco mejor? Realmente no entiendo lo que estás tratando de hacer.
neurona
¿Qué se supone que debe hacer `$ name = $ (($ 6 + 1))`?
PSkocik
@PSkocik que esperaba hacerFIRST=$(( $6 + 1 ))
giannis christofakis

Respuestas:

16

En primer lugar, no puede haber ningún espacio alrededor =en la declaración de variables en bash.

Para obtener lo que quieres puedes usar eval.

Por ejemplo, un script de muestra como el tuyo:

#!/bin/bash
i=0
for name in FIRST SECOND THIRD FOURTH FIFTH; do
    eval "$name"="'$(( $i + 1 ))q;d'"
    printf '%s\n' "${!name}"
    i=$(( $i + 1 ))
done

Impresiones:

1q;d
2q;d
3q;d
4q;d
5q;d

Use con evalprecaución, algunas personas lo llaman malvado por alguna razón válida.

declare funcionaría también:

#!/bin/bash
i=0
for name in FIRST SECOND THIRD FOURTH FIFTH; do
    declare "$name"="$(( $i + 1 ))q;d"
    printf '%s\n' "${!name}"
    i=$(( $i + 1 ))
done

también imprime:

1q;d
2q;d
3q;d
4q;d
5q;d
heemayl
fuente
¿Para qué sirve el !signo de exclamación printf '%s\n' "${!name}"?
giannis christofakis
1
Se llama expansión indirecta de la expansión de bashparámetros
lea
1
Bash también tiene una alternativa más agradable a declare/ eval: printf -v varname '%fmt' args. Algunas funciones internas de finalización de bash lo usan para llamar por referencia. (pase el nombre de una variable para almacenar).
Peter Cordes
Nota: El uso declaresolo establece la variable en el ámbito local, mientras que el evalenfoque la establece globalmente.
usuario
11

Si desea hacer referencia a una variable bash mientras tiene el nombre almacenado en otra variable, puede hacerlo de la siguiente manera:

$ var1=hello
$ var2=var1
$ echo ${!var2}
hello

Usted almacena el nombre de la variable a la que desea acceder, por ejemplo, var2 en este caso. Luego accedes a él con ${!<varable name>}donde <variable name>hay una variable que contiene el nombre de la variable a la que deseas acceder.

Eric Renouf
fuente
Hay una forma portátil con eval var=\$$holderpero evales peligroso!
gavenkoa
1
index=0;                                                                                                                                                                                                           
for name in FIRST SECOND THIRD FOURTH FIFTH; do
    name=$(($index + 1))
    echo "${name}q;d"
    index=$((index+1))
done

¿Es eso lo que estás intentando?

neurona
fuente
1

Lo que obtengo de su código y su salida deseada (corríjame si me equivoco):
No se usan los nombres de variable "PRIMERO" / "SEGUNDO" / ..., solo necesita un bucle con un índice ... .

Esto hará el trabajo:

for i in {1..5} ; do echo $i"q;d" ; done

csny
fuente
Sí, tienes razón, excepto que además quiero hacer una función aritmética con una variable.
giannis christofakis
¿Puedes dar un ejemplo de esta función aritmética? ¿Necesita el nombre de la variable (como "TERCERO") o solo el valor del índice?
csny
SUM=$(($6 + $i)); echo $SUM"q;d", Veo lo que estaba haciendo mal.
giannis christofakis