Passare in rassegna gli array, stampando sia l'indice che il valore


184

Voglio fare qualcosa del genere:

foo=( )
foo[0]="bar"
foo[35]="baz"
for((i=0;i<${#foo[@]};i++))
do
    echo "$i: ${foo[$i]}"
done
# Output:
# 0: bar
# 1: 

Poi ho provato a scorrere attraverso di esso usando per in:

foo=( )
foo[0]="bar"
foo[35]="baz"
for i in ${foo[@]}
do
    echo "?: $i"
done
# Output:
# ?: bar
# ?: naz

ma qui non conosco il valore dell'indice.

So che potresti fare qualcosa del genere

foo=( )
foo[0]="bar"
foo[35]="baz"
declare -p foo
# Output:
# declare -a foo='([0]="bar" [35]="baz")'

ma non puoi farlo in un altro modo?

Risposte:


317

Dovresti trovare le chiavi dell'array con "${!foo[@]}"( riferimento ), quindi:

for i in "${!foo[@]}"; do 
  printf "%s\t%s\n" "$i" "${foo[$i]}"
done

Ciò significa che gli indici saranno presenti $imentre sarà necessario accedere agli elementi stessi${foo[$i]}


6
Nota importante, sebbene iterabile, un elenco di parole separato da spazi non è un array. Avvolgilo in questo modo (a b c)per convertirlo in un array.
Breedly,

4
L'uso di [@]virgolette doppie significa che non è un "elenco di parole separato da spazi". Viene visualizzato l'elenco delle chiavi di matrice effettive, anche se le singole chiavi contengono spazi bianchi.
Glenn Jackman,

@glennjackman puoi spiegarlo di piùThe use of [@] and double quotes means it's not a "space separated list of words"
Kasun Siyambalapitiya il

1
"${foo[@]}"prende la variabile (array) fooe la espande come una matrice, mantenendo l'identità dei suoi elementi, cioè non dividendoli nello spazio bianco. Se foo=(x 'y z'), quindi f "${foo[@]}"chiama fcon due argomenti xe 'y z'. Query di metadati come "${!foo[@]}"e "${#foo[@]}"allo stesso modo fungono fooda array.
BallpointBen,

Risposta giusta, ma quel riferimento è imperscrutabile. Questa risposta spiega semplicemente: " "${!foo[@]}"è l'elenco di tutti gli indici impostati nell'array".
pjhsea,

18

puoi sempre usare l'iterazione param:

ITER=0
for I in ${FOO[@]}
do  
    echo ${I} ${ITER}
    ITER=$(expr $ITER + 1)
done

5
((ITER++))in modern bash
David Tonhofer,

Perché post-incremento? Vuoi solo incrementare il valore, quindi ((++ ITER)) è più direttamente un'istruzione di ciò che vuoi fare ....
MikeW

3
No, non "SEMPRE". Un hash può avere "buchi", il che significa che non tutti i numeri sono un indice. Nel tuo esempio $ {ITER} non è sempre l'indice di $ {I}.
Marco

10
INDEX=0
for i in $list; do 
    echo ${INDEX}_$i
    let INDEX=${INDEX}+1
done

La tua risposta merita sicuramente una piccola spiegazione. Si prega di fare riferimento a stackoverflow.com/help/how-to-answer . I commenti aiuterebbero a creare contenuti ricercabili.
J. Chomel,

3
Mentre questo frammento di codice può risolvere la domanda, inclusa una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro e che queste persone potrebbero non conoscere i motivi del tuo suggerimento sul codice. Cerca anche di non aggiungere il tuo codice a commenti esplicativi, ciò riduce la leggibilità sia del codice che delle spiegazioni!
kayess,

2

In bash 4, puoi usare array associativi:

declare -A foo
foo[0]="bar"
foo[35]="baz"
for key in "${!foo[@]}"
do
    echo "key: $key, value: ${foo[$key]}"
done

# output
# $ key: 0, value bar.
# $ key: 35, value baz.

In bash 3 funziona (funziona anche in zsh):

map=( )
map+=("0:bar")
map+=("35:baz")

for keyvalue in "${map[@]}" ; do
    key=${keyvalue%%:*}
    value=${keyvalue#*:}
    echo "key: $key, value $value."
done

1
users=("kamal" "jamal" "rahim" "karim" "sadia")
index=()
t=-1

for i in ${users[@]}; do
  t=$(( t + 1 ))
  if [ $t -eq 0 ]; then
    for j in ${!users[@]}; do
      index[$j]=$j
    done
  fi
  echo "${index[$t]} is $i"
done

1
Questo è sbagliato! Il circuito interno è inutile e potrebbe portare a risultati sbagliati!
F. Hauri,

1

Semplice trucco di una riga per il dumping array

Ho aggiunto un valore con spazi:

foo=()
foo[12]="bar"
foo[42]="foo bar baz"
foo[35]="baz"

Io, per discarica rapida matrici o matrici associative che uso

Questo comando a una riga:

paste <(printf "%s\n" "${!foo[@]}") <(printf "%s\n" "${foo[@]}")

Renderà:

12  bar
35  baz
42  foo bar baz

spiegato

  • printf "%s\n" "${!foo[@]}"stamperà tutte le chiavi separate da una nuova riga ,
  • printf "%s\n" "${foo[@]}"stamperà tutti i valori separati da una nuova riga ,
  • paste <(cmd1) <(cmd2)unirà l'output di cmd1e cmd2riga per riga.

tunning

Questo potrebbe essere regolato -ddall'interruttore:

paste -d : <(printf "%s\n" "${!foo[@]}") <(printf "%s\n" "${foo[@]}")
12:bar
35:baz
42:foo bar baz

o anche:

paste -d = <(printf "foo[%s]\n" "${!foo[@]}") <(printf "'%s'\n" "${foo[@]}")
foo[12]='bar'
foo[35]='baz'
foo[42]='foo bar baz'

La matrice associativa funzionerà allo stesso modo:

declare -A bar=([foo]=snoopy [bar]=nice [baz]=cool [foo bar]='Hello world!')
paste -d = <(printf "bar[%s]\n" "${!bar[@]}") <(printf '"%s"\n' "${bar[@]}")
bar[foo bar]="Hello world!"
bar[foo]="snoopy"
bar[bar]="nice"
bar[baz]="cool"

Emettere con newline o caratteri speciali

Sfortunatamente, c'è almeno una condizione che rende questo non più funzionante: quando la variabile contiene newline:

foo[17]=$'There is one\nnewline'

Il comando pasteunirà riga per riga, quindi l'output risulterà errato:

paste -d = <(printf "foo[%s]\n" "${!foo[@]}") <(printf "'%s'\n" "${foo[@]}")
foo[12]='bar'
foo[17]='There is one
foo[35]=newline'
foo[42]='baz'
='foo bar baz'

Per questo lavoro, è possibile utilizzare %qinvece %snel secondo printfcomando (e whipe quoting):

paste -d = <(printf "foo[%s]\n" "${!foo[@]}") <(printf "%q\n" "${foo[@]}")

Renderà perfetto:

foo[12]=bar
foo[17]=$'There is one\nnewline'
foo[35]=baz
foo[42]=foo\ bar\ baz

Da man bash:

          %q     causes  printf  to output the corresponding argument in a
                 format that can be reused as shell input.

Il mio voto printf "%q\n" "${var[@]}" positivo per Newline è stato il mio problema!
techno
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.