Ritorno indiretto di tutti gli elementi in un array


10

La pagina man di Bash descrive l'uso di ${!a}per restituire il contenuto della variabile il cui nome è il contenuto di a(un livello di riferimento indiretto).

Mi piacerebbe sapere come restituire tutti gli elementi in un array usando questo, cioè

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

ritorna

one two three

Vorrei per:

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

per restituire lo stesso. Sfortunatamente, non lo fa, ma ritorna 0invece.

Aggiornare

Date le risposte, ora mi rendo conto che il mio esempio era troppo semplice, dato che ovviamente qualcosa del tipo:

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

Raggiungerà esattamente quello che ho detto di cui avevo bisogno.

Quindi, ecco cosa stavo cercando di fare:

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

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

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

Naturalmente, leggere attentamente la pagina man di Bash mostra che questo non funzionerà come previsto perché l'ultima riga restituisce semplicemente gli indici della "matrice" $_LIST(non una matrice).

In ogni caso, quanto segue dovrebbe fare il lavoro (come sottolineato):

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

o ... (il percorso che ho seguito, alla fine):

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

Supponendo, ovviamente, che gli elementi non contengano spazi bianchi.


Aggiungi [@]al puntatore _LIST="LIST_${whichone}[@]"e poi, usa LIST=("${!_LIST}")per copiare l'array. È consigliabile utilizzare i nomi delle variabili minuscole per evitare conflitti con le variabili di ambiente (maiuscole).
Isaac

Risposte:


7

Penso che l'uso del riferimento indiretto della variabile bash debba essere trattato alla lettera.

Per esempio. Per il tuo esempio originale:

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

Per l'ultimo vero scenario, credo che il codice qui sotto farebbe il lavoro.

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

È meglio usare la notazione "${var[@]}"che evita di incasinare l' $IFSespansione dei parametri e. Ecco il codice finale.

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

8

Devi copiare gli elementi esplicitamente. Per un array indicizzato:

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

Per un array associativo (notare che aè il nome della variabile array, non una variabile il cui valore è il nome di una variabile array):

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

Se si dispone del nome della variabile in un array, è possibile utilizzare il metodo elemento per elemento con un passaggio aggiuntivo per recuperare le chiavi.

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

(Attenzione, il codice in questo post è stato digitato direttamente in un browser e non testato.)


Hai ragione, ma sfortunatamente, ciò non risolve il mio problema (il mio esempio era troppo semplicistico). Ho fornito un aggiornamento per essere più chiaro sulla mia intenzione.
Eric Smith,

@Eric Penso che in ksh / bash tu abbia bisogno di eval in quella fase. Vedi la mia modifica.
Gilles 'SO- smetti di essere malvagio' il

+1, ma secondo la tua dichiarazione di non responsabilità, in realtà non funziona del tutto (l'ultimo bit di codice), ma richiede solo alcune piccole modifiche. In particolare, dovrebbe essere \${!$name[@]}sulla prima riga, in modo che la prima espansione sia solo di '$ name', e che ${!a[@]}sia salvata per l'eval, e la stessa cosa nel ciclo for, con \${$name}. Anche le altre due barre rovesciate prima di '$ k' non sono strettamente necessarie.
krb686,

2

${!b[*]}si espande negli indici utilizzati nell'array b.

Cosa vorresti deve essere fatto in due fasi, in modo evalaiuterà: eval echo \${$b[*]}. (Nota \che garantisce che il primo $passi il primo passo, l'espansione variabile e che sarà espanso solo nel secondo passo eval).

Secondo il parametro Expansion !viene utilizzato sia per l'espansione indiretta ( {!a}), per i nomi che corrispondono al prefisso ( ${!a*}) e per l'elenco delle chiavi dell'array ( ${!a[*]}). Poiché l'elenco delle chiavi dell'array ha la stessa sintassi dell'espansione indiretta prevista + espansione dell'elemento dell'array, la versione successiva non è supportata così com'è.


2
${!a}si espande al valore della variabile il cui nome è $a. Questo è piuttosto brevemente descritto nel manuale, nel paragrafo che inizia con "Se il primo carattere del parametro è un punto esclamativo ( !), viene introdotto un livello di indiretto variabile".
Gilles 'SO- smetti di essere malvagio' il

Sì - @Gilles ha ragione, ma @manatwork, in seconda lettura, ho notato che ${!è un po 'ambiguo poiché se si tratta di un array con cui si ha a che fare, il comportamento è diverso.
Eric Smith,

@Gilles hai ragione su quella frase, ma purtroppo non si applica come "Le eccezioni a questo sono le espansioni di $ {! Prefisso *} e $ {! Name [@]} descritte di seguito." Ma la mia risposta è sicuramente un casino ambiguo, quindi lo modificherò.
Manatwork,

0

Per accedere agli array indirettamente, basta aggiungere [@]alla variabile indiretta b=a[@].

Se sono impostate queste variabili:

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

Quindi, questo funzionerà:

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

O semplicemente:

echo "${!b}"

Tale array può essere copiato come questo:

newArr=( "${!b}" )

E poi stampato con:

declare -p newArr

In uno script:

#!/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

Ovviamente, tutto quanto sopra copierà un array non sparse. Uno in cui tutti gli indici hanno un valore.

matrici sparse

Un array sparse è uno che può avere elementi non definiti.
Ad esempio a[8]=1234definisce un elemento e, in bash, non esistono da 0 a 7.

Per copiare tale matrice sparsa utilizzare questo metodo

  1. Stampa il vecchio array:

    $ oldarr[8]=1234
    $ declare -p oldarr
    declare -a oldarr=([8]="1234")
  2. Sostituisci il nome dell'array e acquisisci la stringa:

    $ str=$(declare -p oldarr | sed 's/oldarr=/newarr=/')
  3. Valutare la stringa così creata, il nuovo array è stato definito:

    $ eval "$str"
    $ declare -p newarr
    declare -a newarr=([8]="1234")
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.