bash scripting indirection

bash - ¿Cómo iterar sobre una matriz usando referencia indirecta?



scripting indirection (5)

¿Cómo puedo hacer que este código funcione?

#!/bin/bash ARRAYNAME=''FRUITS'' FRUITS=( APPLE BANANA ORANGE ) for FRUIT in ${!ARRAYNAME[@]} do echo ${FRUIT} done

Este codigo

echo ${!ARRAYNAME[0]}

Imprime APPLE . Estoy tratando de hacer algo similar pero con "[@]" para iterar sobre la matriz.

Gracias por adelantado,


A pesar de la simple pregunta OP, estas respuestas no se escalarán para los casos de uso reales más comunes, es decir, elementos de matriz que contienen espacios en blanco o comodines que aún no deben expandirse a los nombres de archivo.

FRUITS=( APPLE BANANA ORANGE ''not broken'' ''*.h'') ARRAYNAME=FRUITS eval ARRAY=/(/${$ARRAYNAME[@]}/) $ echo "${ARRAY[4]}" broken $ echo "${ARRAY[5]}" config.h $

Esto funciona:

FRUITS=( APPLE BANANA ORANGE ''not broken'' ''*.h'') ARRAYNAME=FRUITS eval ARRAY="(/"/${$ARRAYNAME[@]}/")" $ echo "${ARRAY[3]}" not broken $ echo "${ARRAY[4]}" *.h $

De la misma manera que debe acostumbrarse a usar "$@" no a $@ , siempre cite el interior ( ) para las expansiones de matriz, a menos que desee una expansión de nombre de archivo o sepa que no hay posibilidad de que los elementos de la matriz contengan espacios en blanco.

Haga esto: X=("${Y[@]}")

No esto: X=(${Y[@]})


Aquí hay una manera de hacerlo sin evaluar.

Ver el truco de Bash # 2 descrito aquí: BashFAQ/006

Parece que funciona en bash 3 en adelante.

#!/bin/bash ARRAYNAME=''FRUITS'' tmp=$ARRAYNAME[@] FRUITS=( APPLE BANANA ORANGE "STAR FRUIT" ) for FRUIT in "${!tmp}" do echo "${FRUIT}" done

Aquí hay un ejemplo más realista que muestra cómo pasar una matriz por referencia a una función:

pretty_print_array () { local arrayname=$1 local tmp=$arrayname[@] local array=( "${!tmp}" ) local FS='', '' # Field seperator local var # Print each element enclosed in quotes and separated by $FS printf -v var "/"%s/"$FS" "${array[@]}" # Chop trailing $FS var=${var%$FS} echo "$arrayname=($var)" } FRUITS=( APPLE BANANA ORANGE "STAR FRUIT" ) pretty_print_array FRUITS # prints FRUITS=("APPLE", "BANANA", "ORANGE", "STAR FRUIT")


Sólo quería añadir otro caso de uso útil. Estaba buscando en la web una solución a un problema diferente, pero relacionado

ARRAYNAME=( FRUITS VEG ) FRUITS=( APPLE BANANA ORANGE ) VEG=( CARROT CELERY CUCUMBER ) for I in "${ARRAYNAME[@]}" do array="${I}[@]" for fruit in "${!array}"; do echo $fruit done done


${!ARRAYNAME[@]} significa "los índices de ARRAYNAME ". Como se indica en la página de manual de bash desde que ARRAYNAME está establecido, pero como una cadena, no una matriz, devuelve 0 .

Aquí hay una solución usando eval .

#!/usr/bin/env bash ARRAYNAME=''FRUITS'' FRUITS=( APPLE BANANA ORANGE ) eval array=/( /${${ARRAYNAME}[@]} /) for fruit in "${array[@]}"; do echo ${fruit} done

Lo que originalmente intentabas hacer era crear una referencia indirecta . Estos se introdujeron en la versión 2 de bash y estaban destinados a reemplazar en gran medida la necesidad de eval cuando se intenta lograr un comportamiento similar al de la reflexión en el shell.

Lo que tiene que hacer al usar referencias indirectas con matrices es incluir [@] en su estimación del nombre de la variable:

#!/usr/bin/env bash ARRAYNAME=''FRUITS'' FRUITS=( APPLE BANANA ORANGE ) array="${ARRAYNAME}[@]" for fruit in "${!array}"; do echo $fruit done

Dicho todo esto, una cosa es usar referencias indirectas en este ejemplo trivial, pero, como se indica en el enlace proporcionado por Dennis Williamson, debe dudar en usarlas en scripts del mundo real. Todos están garantizados para hacer que su código sea más confuso de lo necesario. Por lo general, puede obtener la funcionalidad que necesita con una matriz asociativa.


eval ejecuta código que contiene elementos de matriz, incluso si contienen, por ejemplo, sustituciones de comandos. También cambia los elementos de la matriz interpretando los metacaracteres de bash en ellos.

Una herramienta que evita estos problemas es la referencia de declare , consulte man bash en declare:

-n Asigne a cada nombre el atributo nameref, por lo que es una referencia de nombre a otra variable. Esa otra variable está definida por el valor del nombre. Todas las referencias, asignaciones y modificaciones de atributos al nombre, excepto aquellas que usan o cambian el atributo -n en sí, se realizan en la variable referenciada por el valor del nombre. El atributo nameref no se puede aplicar a las variables de matriz.

#!/bin/bash declare -n ARRAYNAME=''FRUITS'' FRUITS=(APPLE BANANA ORANGE "BITTER LEMON") for FRUIT in "${ARRAYNAME[@]}" do echo "${FRUIT}" done