Verificamos completamente cada uno de los artículos en nuestra página web con el objetivo de enseñarte siempre información veraz y actualizada.
Solución:
He estado buscando una mejor manera de hacerlo recientemente. De asociación array sonaba como una exageración para mí. Mira lo que he encontrado:
suffix=bzz
declare prefix_$suffix=mystr
…y luego…
varname=prefix_$suffix
echo $!varname
Utilice un asociativo array, con nombres de comando como keys.
# Requires bash 4, though
declare -A magic_variable=()
function grep_search() tail -1 )
echo $magic_variable[$1]
Si no puede usar matrices asociativas (por ejemplo, debe admitir bash
3), puedes usar declare
para crear nombres de variables dinámicas:
declare "magic_variable_$1=$(ls | tail -1)"
y utilice la expansión de parámetros indirectos para acceder al valor.
var="magic_variable_$1"
echo "$!var"
Consulte BashFAQ: Indirección: evaluación de variables indirectas / de referencia.
Más allá de las matrices asociativas, hay varias formas de lograr variables dinámicas en Bash. Tenga en cuenta que todas estas técnicas presentan riesgos, que se analizan al final de esta respuesta.
En los siguientes ejemplos asumiré que i=37
y que desea asignar un alias a la variable denominada var_37
cuyo valor inicial es lolilol
.
Método 1. Usar una variable de “puntero”
Simplemente puede almacenar el nombre de la variable en una variable de indirección, no muy diferente a un puntero C. Bash luego tiene una sintaxis para leyendo la variable con alias: $!name
se expande al valor de la variable cuyo nombre es el valor de la variable name
. Puedes pensar en ello como una expansión de dos etapas: $!name
se expande a $var_37
, que se expande a lolilol
.
name="var_$i"
echo "$name" # outputs “var_37”
echo "$!name" # outputs “lolilol”
echo "$!name%lol" # outputs “loli”
# etc.
Desafortunadamente, no existe una sintaxis equivalente para modificando la variable con alias. En su lugar, puede lograr la asignación con uno de los siguientes trucos.
1a. Asignar con eval
eval
es malvado, pero también es la forma más sencilla y portátil de lograr nuestro objetivo. Debe escapar con cuidado del lado derecho de la tarea, ya que se evaluará dos veces. Una forma fácil y sistemática de hacer esto es evaluar el lado derecho de antemano (o utilizar printf %q
).
Y debe verificar manualmente que el lado izquierdo sea un nombre de variable válido, o un nombre con índice (¿y si fuera evil_code #
?). Por el contrario, todos los demás métodos a continuación lo hacen cumplir automáticamente.
# check that name is a valid variable name:
# note: this code does not support variable_name[index]
shopt -s globasciiranges
[[ "$name" == [a-zA-Z_]*([a-zA-Z_0-9]) ]] || exit
value='babibab'
eval "$name"='$value' # carefully escape the right-hand side!
echo "$var_37" # outputs “babibab”
Desventajas:
- no comprueba la validez del nombre de la variable.
eval
es malvado.eval
es malvado.eval
es malvado.
1b. Asignar con read
los read
builtin le permite asignar valores a una variable a la que le da el nombre, un hecho que puede explotarse junto con here-strings:
IFS= read -r -d '' "$name" <<< 'babibab'
echo "$var_37" # outputs “babibabn”
los IFS
parte y la opcion -r
asegúrese de que el valor se asigne tal cual, mientras que la opción -d ''
permite asignar valores multilínea. Debido a esta última opción, el comando regresa con un código de salida distinto de cero.
Tenga en cuenta que, dado que estamos usando unstring, se agrega un carácter de nueva línea al valor.
Desventajas:
- algo oscuro;
- regresa con un código de salida distinto de cero;
- agrega una nueva línea al valor.
1c. Asignar con printf
Desde Bash 3.1 (lanzado en 2005), el printf
builtin también puede asignar su resultado a una variable cuyo nombre se proporciona. A diferencia de las soluciones anteriores, simplemente funciona, no se necesita ningún esfuerzo adicional para escapar de las cosas, para evitar que se rompan, etc.
printf -v "$name" '%s' 'babibab'
echo "$var_37" # outputs “babibab”
Desventajas:
- Menos portátil (pero, bueno).
Método 2. Usar una variable de "referencia"
Desde Bash 4.3 (lanzado en 2014), el declare
incorporado tiene una opción -n
para crear una variable que sea una "referencia de nombre" a otra variable, muy similar a las referencias de C ++. Al igual que en el Método 1, la referencia almacena el nombre de la variable con alias, pero cada vez que se accede a la referencia (ya sea para leer o asignar), Bash resuelve automáticamente la indirección.
Además, Bash tiene una sintaxis especial y muy confusa para obtener el valor de la referencia en sí, juzgue usted mismo: $!ref
.
declare -n ref="var_$i"
echo "$!ref" # outputs “var_37”
echo "$ref" # outputs “lolilol”
ref='babibab'
echo "$var_37" # outputs “babibab”
Esto no evita las trampas que se explican a continuación, pero al menos simplifica la sintaxis.
Desventajas:
- No es portátil.
Riesgos
Todas estas técnicas de aliasing presentan varios riesgos. El primero es ejecutar código arbitrario cada vez que resuelve la indirección (ya sea para leer o para asignar). De hecho, en lugar de un nombre de variable escalar, como var_37
, también puede alias un array subíndice, como arr[42]
. Pero Bash evalúa el contenido de los corchetes cada vez que es necesario, por lo que el alias arr[$(do_evil)]
tendrá efectos inesperados ... Como consecuencia, solo use estas técnicas cuando controle la procedencia del alias.
function guillemots()
declare -n var="$1"
var="«$var»"
arr=( aaa bbb ccc )
guillemots 'arr[1]' # modifies the second cell of the array, as expected
guillemots 'arr[$(date>>date.out)1]' # writes twice into date.out
# (once when expanding var, once when assigning to it)
El segundo riesgo es crear un alias cíclico. Como las variables de Bash se identifican por su nombre y no por su alcance, puede crear inadvertidamente un alias para sí mismo (mientras piensa que crearía un alias de una variable de un alcance adjunto). Esto puede suceder en particular cuando se utilizan nombres de variables comunes (como var
). Como consecuencia, solo use estas técnicas cuando controle el nombre de la variable con alias.
function guillemots()
# var is intended to be local to the function,
# aliasing a variable which comes from outside
declare -n var="$1"
var="«$var»"
var='lolilol'
guillemots var # Bash warnings: “var: circular name reference”
echo "$var" # outputs anything!
Fuente:
- BashFaq / 006: ¿Cómo puedo usar variables variables (variables indirectas, punteros, referencias) o matrices asociativas?
- BashFAQ / 048: comando eval y problemas de seguridad
Si te animas, eres capaz de dejar una noticia acerca de qué le añadirías a este artículo.