2012-06-24 31 views
15

Come posso far funzionare questo codice?Come eseguire iterazioni su un array usando un riferimento indiretto?

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

Questo codice:

echo ${!ARRAYNAME[0]} 

Stampe APPLE. Sto cercando di fare qualcosa di simile ma con "[@]" per scorrere l'array.

Grazie in anticipo,

+4

Si prega di vedere [BashFAQ/006] (http://mywiki.wooledge.org/BashFAQ/006). –

risposta

18

${!ARRAYNAME[@]} mezzi "gli indici di ARRAYNAME". Come indicato nello bash man page dal momento che è impostato ARRAYNAME, ma come stringa, non come array, restituisce 0.

Ecco una soluzione utilizzando eval.

#!/usr/bin/env bash 

ARRAYNAME='FRUITS' 
FRUITS=(APPLE BANANA ORANGE) 

eval array=\(\${${ARRAYNAME}[@]} \) 

for fruit in "${array[@]}"; do 
    echo ${fruit} 
done 

Cosa si erano originariamente cercando di fare era creare un Indirect Reference. Questi sono stati introdotti in bash versione 2 e sono stati pensati per sostituire in gran parte la necessità di eval quando si cerca di ottenere un comportamento simile alla riflessione nella shell.

Quello che devi fare quando si utilizza riferimenti indiretti con gli array è includere il [@] nel vostro indovinare il nome della variabile:

#!/usr/bin/env bash 

ARRAYNAME='FRUITS' 
FRUITS=(APPLE BANANA ORANGE) 

array="${ARRAYNAME}[@]" 
for fruit in "${!array}"; do 
    echo $fruit 
done 

Detto questo, è una cosa da usare riferimenti indiretti a questo esempio banale, ma, come indicato nel link fornito da Dennis Williamson, dovresti esitare a usarli nelle sceneggiature del mondo reale. Sono tutti ma garantiti per rendere il tuo codice più confuso del necessario. Di solito è possibile ottenere la funzionalità necessaria con una matrice associativa.

+0

Tim: la seconda parte della risposta non è utile, perché ARRAYNAME è un parametro di input del mio script. Ma la tua prima soluzione mi ha permesso di trovare una soluzione più elegante: ARRAY = $ {! ARRAYNAME}; per FRUIT in $ {ARRAY [@]} ... Grazie – Neuquino

+0

@Neuquino Vedi la mia risposta modificata. Tutto ciò che devi fare è concatenare '[@]' alla fine della tua variabile di input. –

+2

@Neuquino Se ARRAYNAME è un parametro di input per il tuo script, allora stai facendo qualcosa di terribilmente sbagliato. Non ci sono motivi per mescolare l'input dell'utente con nomi di variabili. In questo caso (combinato con espansione indiretta), consente l'iniezione di codice arbitrario. L'UNICO motivo valido per queste tecniche è per l'uso in funzioni, mai nell'ambito globale e mai combinato con l'input dell'utente. – ormaaj

0

Volevo solo aggiungere un altro caso d'uso utile. Ero alla ricerca sul web per una soluzione di un diverso, ma connesso problema

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 
+0

'array =" $ {I} [@] "' è esattamente uguale a 'tmp = $ arrayname [@]'! Non c'è miglioramento! –

0

Nonostante la semplice domanda OP, queste risposte non scala per le più comuni, reali casi d'uso, cioè, gli elementi dell'array contiene spazi o caratteri jolly che non dovrebbero ancora essere espansi in nomi di file.

FRUITS=(APPLE BANANA ORANGE 'not broken' '*.h') 
ARRAYNAME=FRUITS 
eval ARRAY=\(\${$ARRAYNAME[@]}\) 

$ echo "${ARRAY[4]}" 
broken 
$ echo "${ARRAY[5]}" 
config.h 
$ 

Questo funziona:

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

$ echo "${ARRAY[3]}" 
not broken 
$ echo "${ARRAY[4]}" 
*.h 
$ 

Proprio come si dovrebbe prendere l'abitudine di usare "[email protected]" non [email protected], sempre citare all'interno () per espansioni di array, a meno che non si vuole espansione dei nomi o sapere non c'è possibilità di elementi dell'array che contengono spazi bianchi.

fare questo: X=("${Y[@]}")

Non questo: X=(${Y[@]})

8

Ecco un modo per farlo senza eval.

Vedi Bash trucco # 2 descritto qui: http://mywiki.wooledge.org/BashFAQ/006

sembra funzionare in bash 3 in su.

#!/bin/bash 

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

Ecco un esempio più realistico che mostra come passare una matrice per riferimento ad una funzione:

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") 
+0

un modo per ottenere la dimensione di tale array indiretto anche eval? –

0

EVAL esegue codice contenente elementi dell'array, anche se contengono, per esempio, sostituzioni di comando. Cambia anche gli elementi dell'array interpretando i metacaratteri di bash in essi.

Il singolo strumento che evita questi problemi è un riferimento :

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