web-dev-qa-db-fra.com

Faire une boucle 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 en quelque sorte de parcourir les tuples?

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

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

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

Le IFS="," définit le séparateur de champ 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
72
Eduardo Ivanec

Je crois que cette solution est un peu plus propre que les autres qui ont été soumises, h/t à this guide de style bash pour illustrer comment la lecture peut être utilisée pour diviser des chaînes à un délimiteur et les affecter à des variables individuelles .

for i in c,3 e,5; do 
    IFS=',' read item1 item2 <<< "${i}"
    echo "${item1}" and "${item2}"
done
16
Grant Humphries
c=('a' 'c')
n=(3    4 )

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

Pourrait parfois être plus pratique.

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

seq 0 2 produit la séquence de nombres 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)) est 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 annulons tout: 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.

7
user unknown
$ echo 'c,3;e,5;' | while IFS=',' read -d';' i j; do echo "$i and $j"; done
c and 3
e and 5
6
kev

Utilisez un tableau associatif (également appelé 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 pensez qu'un jour vous aurez besoin de triplets 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
6
VasiliNovikov

Sur la base de la réponse donnée par @ eduardo-ivanec sans paramétrer/réinitialiser le IFS, on pourrait simplement 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
5
MZHm

Utilisation de GNU Parallel:

parallel echo {1} and {2} ::: c e :::+ 3 5

Ou:

parallel -N2 echo {1} and {2} ::: c 3 e 5

Ou:

parallel --colsep , echo {1} and {2} ::: c,3 e,5
2
Ole Tange

Un peu plus impliqué, mais peut être utile:

a='((c,3), (e,5))'
IFS='()'; for t in $a; do [ -n "$t" ] && { IFS=','; set -- $t; [ -n "$1" ] && echo i=$1 j=$2; }; done
0
do echo $key $value
done < file_discriptor

par exemple:

$ while read key value; do echo $key $value ;done <<EOF
> c 3
> e 5
> EOF
c 3
e 5

$ echo -e 'c 3\ne 5' > file

$ while read key value; do echo $key $value ;done <file
c 3
e 5

$ echo -e 'c,3\ne,5' > file

$ while IFS=, read key value; do echo $key $value ;done <file
c 3
e 5
0
prodriguez903