262 votes

Boucle sur des tableaux, impression de l'index et de la valeur

Je veux faire quelque chose comme ça :

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

Puis j'ai essayé de le parcourir en boucle en utilisant for in :

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

mais ici je ne connais pas la valeur de l'index.

Je sais que tu pourrais faire quelque chose comme

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

mais, ne pouvez-vous pas le faire d'une autre manière ?

450voto

glenn jackman Points 69748

Vous trouverez les clés du tableau avec "${!foo[@]}" ( référence ), donc :

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

Ce qui signifie que les indices seront dans $i tandis que les éléments eux-mêmes doivent être accessibles via ${foo[$i]}

7 votes

Remarque importante : bien qu'elle soit itérable, une liste de mots séparés par des espaces est une liste de mots. pas un tableau. Enveloppez-le comme suit (a b c) pour le convertir en tableau.

5 votes

L'utilisation de [@] et les guillemets doubles signifient qu'il ne s'agit pas d'une "liste de mots séparés par des espaces". Vous obtenez la liste des clés réelles du tableau, même si les clés individuelles contiennent des espaces.

0 votes

@glennjackman pouvez-vous expliquer cela davantage ? The use of [@] and double quotes means it's not a "space separated list of words"

38voto

Eyal Ch Points 186

Vous pouvez toujours utiliser le paramètre d'itération :

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

12 votes

((ITER++)) en bash moderne

0 votes

Pourquoi une post-incrémentation ? Vous voulez seulement que la valeur soit incrémentée, donc ((++ITER)) est plus directement une déclaration de ce que vous voulez faire .....

4 votes

Non, pas "TOUJOURS". Un hachage peut avoir des "trous", ce qui signifie que tous les chiffres ne sont pas des index. Dans votre exemple, le ${ITER} n'est pas toujours l'index du ${I}.

27voto

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

0 votes

Votre réponse mérite certainement une petite explication. Veuillez vous référer à stackoverflow.com/help/how-to-answer . Les commentaires aideraient à créer un contenu consultable.

3 votes

Bien que cet extrait de code puisse résoudre la question, y compris une explication contribue vraiment à améliorer la qualité de votre article. N'oubliez pas que vous répondez à la question pour les lecteurs à venir, et que ces personnes pourraient ne pas connaître les raisons de votre suggestion de code. Essayez également de ne pas encombrer votre code de commentaires explicatifs, cela réduit la lisibilité du code et des explications !

1 votes

J'ai aimé la concision de la réponse et de la stratégie. Pour ceux qui se posent la question, il s'agit de bash et la stratégie consiste à garder la trace de l'index du tableau par vous-même.

9voto

F. Hauri Points 5893

Simple une ligne astuce pour vider un tableau

J'ai ajouté une valeur avec des espaces :

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

Moi, je dépose rapidement bash ou tableaux associatifs J'utilise

Cette commande en une ligne :

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

Rendu :

12  bar
35  baz
42  foo bar baz

Expliqué

  • printf "%s\n" "${!foo[@]}" imprimera tous les clés séparés par un nouvelle ligne ,
  • printf "%s\n" "${foo[@]}" imprimera tous les valeurs séparés par un nouvelle ligne ,
  • paste <(cmd1) <(cmd2) fusionnera la sortie de cmd1 et cmd2 ligne par ligne.

Tunning

Cela pourrait être réglé par -d interrupteur :

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

ou même :

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

Un tableau associatif fonctionnera de la même manière :

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"

Question avec nouvelles lignes ou des caractères spéciaux

Malheureusement, il existe au moins une condition qui fait que cela ne fonctionne plus : lorsque la variable contient une nouvelle ligne :

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

Commande paste va fusionner ligne par ligne, donc la sortie sera fausse :

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'

Pour ce travail, vous pourriez utiliser %q au lieu de %s en second lieu printf (et la citation) :

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

Le rendu sera parfait :

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

De man bash :

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

0 votes

Mon vote pour printf "%q\n" "${var[@]}" nouvelle ligne était mon problème !

7voto

mattmc3 Points 6768

En bash 4, vous pouvez utiliser des tableaux associatifs :

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.

En bash 3, cela fonctionne (fonctionne aussi en zsh) :

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

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

0 votes

En essayant la version bash 3 (dans zsh), il fait écho à l'adresse suivante value pour la clé et la valeur..

1 votes

J'ai vérifié que la deuxième version fonctionne bien pour moi dans Zsh 5.8, cependant, la première version est préférable si vous utilisez Zsh. Remplacer for key in "${!foo[@]}" avec for key in "${(k)foo[@]}" pour Zsh.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X