Est-il possible de boucler sur des tuples dans bash?
À titre d'exemple, ce serait formidable si ce qui suit fonctionnait:
for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done
Existe-t-il une solution de contournement qui me permet en quelque sorte de boucler sur les tuples?
Réponses:
$ 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
(deman builtins
):Le
IFS=","
définit le séparateur de champ afin que chacun$i
soit segmenté$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
la source
IFS
doit être enregistré et réinitialisé à sa valeur d'origine si cela est exécuté sur la ligne de commande. En outre, le nouveauIFS
peut être défini une fois, avant l'exécution de la boucle, plutôt qu'à chaque itération.set -- $i
IFS
, ne régler pour laset
commande:for i in c,3 e,5; do IFS="," set -- $i; echo $1 and $2; done
. Veuillez modifier votre réponse: si tous les lecteurs choisissent une seule des solutions répertoriées, il est inutile de lire l'historique complet du développement. Merci pour cette super astuce!tuples="a,1 b,2 c,3"
et metsIFS=','
comme dans la version modifiée, et au lieu de l'c,3 e,5
utiliser,$tuples
il ne s'imprime pas du tout. Mais au lieu de cela, si je metsIFS=','
juste après ledo
mot - clé dans la boucle for, cela fonctionne bien lorsque vous utilisez$tuples
ainsi que des valeurs littérales. Je pensais juste que ça valait la peine de le dire.IFS
pour diviser les itérations. c'est-à-dire que si vous bouclez sur un tableau commearr=("c,3" "e,5")
et que vous placezIFS
avant la boucle for, la valeur de$i
sera justec
ete
, elle sera séparée3
et ne sera5
doncset
pas analysée correctement car elle$i
n'aura rien à analyser. Cela signifie que si les valeurs à itérer ne sont pas insérées, leIFS
doit être placé à l'intérieur de la boucle et la valeur extérieure doit respecter le séparateur prévu pour la variable sur laquelle itérer. Dans les cas,$tuples
il devrait être simplementIFS=
ce qui est par défaut et se divise sur des espaces.Je pense que cette solution est un peu plus propre que les autres qui ont été soumises, h / t à ce guide de style bash pour illustrer comment read peut être utilisé 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
la source
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:
la source
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 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
la source
GNU bash, version 4.4.23(1)-release-(x86_64-apple-darwin17.5.0)
, qui a été installé via brew, donc YMMV.GNU bash, version 4.3.11(1)-release-(x86_64-pc-linux-gnu)
partir d'Ubuntu 14.04 dans le conteneur Docker.-A
nous avons-a
.declare -a indices=(1 2 3); declare -a sound=(barks purrs cheeps); declare -a size=(big medium small)
etc. Je ne l'ai pas encore essayé dans le terminal, mais je pense que cela devrait fonctionner.c=('a' 'c') n=(3 4 ) for i in $(seq 0 $((${#c[*]}-1))) do echo ${c[i]} ${n[i]} done
Cela peut parfois être plus pratique.
Pour expliquer la
ugly
partie, 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))
?$ ((
${#c[*]}-1
quelque chose )) est une expansion arithmétique, donc $ ((3 + 4)) vaut 7 etc. Notre expression est donc quelque chose - 1. Assez simple, si nous savons ce que${#c[*]}
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)))
c'estfor i in $(seq 0 $((2-1)))
c'estfor i in $(seq 0 1)
c'estfor i in 0 1
. Parce que le dernier élément du tableau a un index qui est la longueur du tableau - 1.la source
for i in $(seq 0 $(($#c[*]}-1))); do [...]
$ echo 'c,3;e,5;' | while IFS=',' read -d';' i j; do echo "$i and $j"; done c and 3 e and 5
la source
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
la source
gnu parallel
brew install parallel
Utilisation
printf
dans une substitution de processus:while read -r k v; do echo "Key $k has value: $v" done < <(printf '%s\n' 'key1 val1' 'key2 val2' 'key3 val3')
Ci-dessus oblige
bash
. Sibash
n'est pas utilisé, utilisez un pipeline simple:printf '%s\n' 'key1 val1' 'key2 val2' 'key3 val3' | while read -r k v; do echo "Key $k has value: $v"; done
la source
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
la source
Un peu plus compliqué, 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
la source
Mais que se passe-t-il si le tuple est supérieur au k / v qu'un tableau associatif peut contenir? Et si c'était 3 ou 4 éléments? On pourrait développer ce concept:
###--------------------------------------------------- ### VARIABLES ###--------------------------------------------------- myVars=( 'ya1,ya2,ya3,ya4' 'ye1,ye2,ye3,ye4' 'yo1,yo2,yo3,yo4' ) ###--------------------------------------------------- ### MAIN PROGRAM ###--------------------------------------------------- ### Echo all elements in the array ###--- printf '\n\n%s\n' "Print all elements in the array..." for dataRow in "${myVars[@]}"; do while IFS=',' read -r var1 var2 var3 var4; do printf '%s\n' "$var1 - $var2 - $var3 - $var4" done <<< "$dataRow" done
Ensuite, la sortie ressemblerait à quelque chose comme:
$ ./assoc-array-tinkering.sh Print all elements in the array... ya1 - ya2 - ya3 - ya4 ye1 - ye2 - ye3 - ye4 yo1 - yo2 - yo3 - yo4
Et le nombre d'éléments est désormais illimité. Ne pas chercher de votes; penser à voix haute. REF1 , REF2
la source
Dans les cas où mes définitions de tuple sont plus complexes, je préfère les avoir dans un heredoc:
while IFS=", " read -ra arr; do echo "${arr[0]} and ${arr[1]}" done <<EOM c, 3 e, 5 EOM
Cela combine la boucle sur les lignes d'un heredoc avec la division des lignes à un caractère de séparation souhaité .
la source