130 votes

Boucler sur des tuples en bash ?

Est-il possible de boucler sur des tuples en bash ?

À titre d'exemple, ce serait formidable si les éléments suivants fonctionnaient :

for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done

Existe-t-il une solution de contournement qui me permette de boucler sur les tuples ?

7 votes

Je viens d'un milieu python et cette question est très utile !

5 votes

En regardant ceci quatre ans plus tard, je me demande s'il n'y a toujours pas de meilleure façon de faire. omg.

0 votes

Presque 8 ans plus tard, je me suis également demandé s'il n'y avait toujours pas de meilleure façon de procéder. Mais cette réponse de 2018 me semble plutôt bonne : stackoverflow.com/a/52228219/463994

107voto

Eduardo Ivanec Points 6244
$ for i in c,3 e,5; do IFS=","; set -- $i; echo $1 and $2; done
c and 3
e and 5

A propos de cette utilisation de set (de man builtins ) :

Tous les arguments restants après le traitement de l'option sont traités comme des valeurs pour les paramètres positionnels et sont assignés, dans l'ordre, à $1, $2, ... $n

Le site IFS="," définit le séparateur de champs de sorte que chaque $i est segmenté en $1 et $2 correctement.

Via ce blog .

Edit : version plus correcte, comme suggéré par @SLACEDIAMOND :

$ OLDIFS=$IFS; IFS=','; for i in c,3 e,5; do set -- $i; echo $1 and $2; done; IFS=$OLDIFS
c and 3
e and 5

77voto

MZHm Points 1094

Sur la base de la réponse donnée par @eduardo-ivanec, sans régler/réinitialiser la IFS on pourrait simplement le faire :

for i in "c 3" "e 5"
do
    set -- $i
    echo $1 and $2
done

Le résultat :

c and 3
e and 5

3 votes

Cette approche me semble beaucoup plus simple que l'approche acceptée et la plus votée. Y a-t-il une raison de ne pas le faire de cette manière plutôt que de la manière suggérée par @Eduardo Ivanec ?

0 votes

@spurra cette réponse est 6 ans et ½ plus récente, et basée sur elle. Le crédit où il est dû.

1 votes

@Diego Je suis au courant de cela. C'est explicitement écrit dans la réponse. Je demandais s'il y avait une raison de ne pas utiliser cette approche plutôt que la réponse acceptée.

54voto

Grant Humphries Points 1023

Ce site Le guide de style bash illustre comment read peut être utilisé pour séparer les chaînes de caractères au niveau d'un délimiteur et les affecter à des variables individuelles. Ainsi, en utilisant cette technique, vous pouvez analyser la chaîne de caractères et affecter les variables avec une ligne unique comme celle de la boucle ci-dessous :

for i in c,3 e,5; do 
    IFS=',' read item1 item2 <<< "${i}"
    echo "${item1}" and "${item2}"
done

21voto

Vasya Novikov Points 1670

Utilisez un tableau associatif (également connu sous le nom de dictionnaire/hashMap) :

declare -A pairs=(
  [c]=3
  [e]=5
)
for key in "${!pairs[@]}"; do
  value="${pairs[$key]}"
  echo "key is $key and value is $value"
done

Fonctionne pour bash4.0+.


Si vous avez besoin de triples au lieu de paires, vous pouvez utiliser l'approche plus générale :

animals=(dog cat mouse)
declare -A sound=(
  [dog]=barks
  [cat]=purrs
  [mouse]=cheeps
)
declare -A size=(
  [dog]=big
  [cat]=medium
  [mouse]=small
)
for animal in "${animals[@]}"; do
  echo "$animal ${sound[$animal]} and it is ${size[$animal]}"
done

1 votes

Pour info, cela n'a pas fonctionné pour moi sur Mac avec GNU bash, version 4.4.23(1)-release-(x86_64-apple-darwin17.5.0) qui a été installé via brew, donc YMMV.

0 votes

Il a cependant fonctionné sur GNU bash, version 4.3.11(1)-release-(x86_64-pc-linux-gnu) depuis Ubuntu 14.04 dans un conteneur docker.

0 votes

Il semble que les anciennes versions de bash ou celles qui ne supportent pas cette fonctionnalité fonctionnent sur la base d'un index ? où la clé est un nombre plutôt qu'une chaîne. tldp.org/LDP/abs/html/declareref.html et au lieu de -A nous avons -a .

9voto

user unknown Points 15555
c=('a' 'c')
n=(3    4 )

for i in $(seq 0 $((${#c[*]}-1)))
do
    echo ${c[i]} ${n[i]}
done

C'est parfois plus pratique.

Pour expliquer le ugly partie, comme indiqué dans les commentaires :

seq 0 2 produit la séquence de chiffres 0 1 2. $(cmd) est une substitution de commande, donc pour cet exemple la sortie de seq 0 2 qui est la séquence de nombres. Mais quelle est la limite supérieure, la $((${#c[*]}-1)) ?

$((quelque chose)) est une expansion arithmétique, donc $((3+4)) vaut 7, etc. Notre expression est ${#c[*]}-1 donc quelque chose - 1. Assez simple, si nous savons ce que ${#c[*]} est.

c est un tableau, c[*] est juste le tableau entier, ${#c[*]} est la taille du tableau qui est 2 dans notre cas. Maintenant, nous faisons marche arrière : for i in $(seq 0 $((${#c[*]}-1))) est for i in $(seq 0 $((2-1))) est for i in $(seq 0 1) est for i in 0 1 . Parce que le dernier élément du tableau a un index qui est la longueur du tableau - 1.

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