Retorno indirecto de todos los elementos en una matriz.

10

La página de manual de Bash describe el uso de ${!a}para devolver el contenido de la variable cuyo nombre es el contenido de a(un nivel de indirección).

Me gustaría saber cómo devolver todos los elementos en una matriz usando esto, es decir,

a=(one two three)
echo ${a[*]}

devoluciones

one two three

Me gustaria para:

b=a
echo ${!b[*]}

para devolver lo mismo. Desafortunadamente, no lo hace, pero regresa en su 0lugar.

Actualizar

Dadas las respuestas, ahora me doy cuenta de que mi ejemplo era demasiado simple, ya que, por supuesto, algo así como:

b=("${a[@]}")

Logrará exactamente lo que dije que necesitaba.

Entonces, esto es lo que estaba tratando de hacer:

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST=LIST_$whichone
LIST=${!_LIST[*]}

Por supuesto, leer cuidadosamente la página de manual de Bash muestra que esto no funcionará como se esperaba porque la última línea simplemente devuelve los índices de la "matriz" $_LIST(no una matriz en absoluto).

En cualquier caso, lo siguiente debería hacer el trabajo (como se señaló):

LIST=($(eval echo \${$_LIST[*]}))

o ... (la ruta que tomé, eventualmente):

LIST_lys="lys1 lys2"
...
LIST=(${!_LIST})

Suponiendo, por supuesto, que los elementos no contienen espacios en blanco.

Eric Smith
fuente
Agregue [@]al puntero _LIST="LIST_${whichone}[@]"y luego, use LIST=("${!_LIST}")para copiar la matriz. Es una buena idea usar nombres de variables en minúsculas para evitar conflictos con las variables de entorno (mayúsculas).
Isaac

Respuestas:

7

Creo que el uso de la referencia indirecta de la variable bash debería tratarse literalmente.

P.ej. Para su ejemplo original:

a=(one two three)
echo ${a[*]} # one two three
b=a
echo ${!b[*]} # this would not work, because this notation 
              # gives the indices of the variable b which
              # is a string in this case and could be thought
              # as a array that conatins only one element, so
              # we get 0 which means the first element
c='a[*]'
echo ${!c} # this will do exactly what you want in the first
           # place

Para el último escenario real, creo que el siguiente código haría el trabajo.

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[*]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one

Es mejor usar notación "${var[@]}"que evite estropear la $IFSexpansión de parámetros y. Aquí está el código final.

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[@]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one
                     # It is essential to have ${!_LIST} quoted
weynhamz
fuente
8

Necesita copiar los elementos explícitamente. Para una matriz indexada:

b=("${a[@]}")

Para una matriz asociativa (tenga en cuenta que aes el nombre de la variable de matriz, no una variable cuyo valor es el nombre de una variable de matriz):

typeset -A b
for k in "${!a[@]}"; do b[$k]=${a[$k]}; done

Si tiene el nombre de la variable en una matriz, puede usar el método elemento por elemento con un paso adicional para recuperar las claves.

eval "keys=(\"\${!$name[@]}\")"
for k in "${keys[@]}"; do eval "b[\$k]=\${$name[\$k]}"; done

(Advertencia, el código de esta publicación se escribió directamente en un navegador y no se probó).

Gilles 'SO- deja de ser malvado'
fuente
Tienes toda la razón, pero desafortunadamente, eso no resuelve mi problema (mi ejemplo fue demasiado simplista). He proporcionado una actualización para ser más claro sobre mi intención.
Eric Smith
@Eric Creo que en ksh / bash necesitas evaluar en esa etapa. Mira mi edición.
Gilles 'SO- deja de ser malvado'
+1, pero según su descargo de responsabilidad, esto realmente no funciona (el último bit de código), pero solo necesita algunos ajustes menores. Específicamente, debe estar \${!$name[@]}en la primera línea, de modo que la primera expansión sea solo de '$ name', y ${!a[@]}se guarde para la evaluación, y lo mismo en el ciclo for, con \${$name}. Las otras dos barras invertidas antes de '$ k' tampoco son estrictamente necesarias allí.
krb686
2

${!b[*]}se expande a los índices utilizados en la matriz b.

Lo que le gustaría se tiene que hacer en dos pasos, por lo que evalle ayudará a: eval echo \${$b[*]}. (Tenga en cuenta \que garantiza que el primero $pasará el primer paso, la expansión variable, y solo se expandirá en el segundo paso eval).

Según el parámetro, la expansión !se usa para la expansión indirecta ( {!a}), el prefijo de coincidencia de nombres ( ${!a*}) y la lista de teclas de matriz ( ${!a[*]}). Debido a que la Lista de claves de matriz tiene la misma sintaxis que su expansión indirecta prevista + expansión de elemento de matriz, la última no es compatible como está.

hombre trabajando
fuente
2
${!a}se expande al valor de la variable cuyo nombre es $a. Esto se describe de manera bastante tersa en el manual, en el párrafo que comienza con "Si el primer carácter del parámetro es un signo de exclamación ( !), se introduce un nivel de indirección variable".
Gilles 'SO- deja de ser malvado'
Sí, @Gilles tiene razón, pero @manatwork, en segunda lectura, me di cuenta de que ${!es un poco ambiguo ya que si se trata de una matriz, el comportamiento es diferente.
Eric Smith
@Gilles tiene razón en esa frase, pero lamentablemente no se aplica como "Las excepciones a esto son las expansiones de $ {! Prefix *} y $ {! Name [@]} que se describen a continuación". Pero mi respuesta es ciertamente un desastre ambiguo, así que lo editaré.
manatwork
0

Para acceder a las matrices indirectamente, solo agregue [@]a la variable indirecta b=a[@].

Si se establecen estas variables:

a=(one two three)
printf '<%s> ' "${a[@]}"; echo

Entonces, esto funcionará:

b="a[@]"
printf '<%s> ' "${!b}"

O simplemente:

echo "${!b}"

Tal matriz podría copiarse así:

newArr=( "${!b}" )

Y luego impreso con:

declare -p newArr

En un guión:

#!/bin/bash
a=(one two three)
echo "original array"
printf '<%s> ' "${a[@]}"; echo

echo "Indirect array with variable b=a[@]"
b="a[@]"
printf '<%s> ' "${!b}"; echo

echo "New array copied in newArr, printed with declare"
newArr=( "${!b}" )
declare -p newArr

Por supuesto, todo lo anterior copiará una matriz no dispersa. Uno en el que todos los índices tienen un valor.

matrices dispersas

Una matriz dispersa es aquella que puede tener elementos no definidos.
Por ejemplo, a[8]=1234define un elemento y, en bash, 0 a 7 no existen.

Para copiar dicha matriz dispersa, use este método

  1. Imprima la matriz anterior:

    $ oldarr[8]=1234
    $ declare -p oldarr
    declare -a oldarr=([8]="1234")
  2. Reemplace el nombre de la matriz y capture la cadena:

    $ str=$(declare -p oldarr | sed 's/oldarr=/newarr=/')
  3. Evalúe la cadena así creada, se ha definido la nueva matriz:

    $ eval "$str"
    $ declare -p newarr
    declare -a newarr=([8]="1234")
Isaac
fuente