Tournez cent tours, tournez mille tours,
Tournez souvent et tournez toujours...
-- Verlaine, « Chevaux de bois »
Commandes affectant le comportement des boucles
Les commandes de contrôle de boucle break et continue [52] correspondent exactement à leur contre-partie dans d'autres langages de programmation. La commande break termine la boucle (en sort), tandis que continue provoque un saut jusqu'à la prochaine itération de la boucle, dans tenir compte des commandes qui restent dans ce cycle particulier de la boucle.
Exemple 11.20. Effets de break et continue dans une boucle
#!/bin/bash LIMITE=19 # Limite haute. echo echo "Affiche les nombres de 1 à 20 (mais pas 3 et 11)." a=0 while [ $a -le "$LIMITE" ] do a=$(($a+1)) if [ "$a" -eq 3 ] || [ "$a" -eq 11 ] # Exclut 3 et 11. then continue # Continue avec une nouvelle itération de la boucle. fi echo -n "$a " # Ceci ne s'exécutera pas pour 3 et 11. done # Exercice : # Pourquoi la boucle affiche-t'elle jusqu'au 20 ? echo; echo echo "Affiche les nombres de 1 à 20, mais quelque chose se passe après 2." ################################################################## # Même boucle, mais en substituant 'continue' avec 'boucle'. a=0 while [ "$a" -le "$LIMITE" ] do a=$(($a+1)) if [ "$a" -gt 2 ] then break # Ne continue pas le reste de la boucle. fi echo -n "$a " done echo; echo; echo exit 0
La commande break peut de façon optionnelle prendre un paramètre. Un simple break termine seulement la boucle interne où elle est incluse mais un break N sortira de N niveaux de boucle.
Exemple 11.21. Sortir de plusieurs niveaux de boucle
#!/bin/bash # break-levels.sh: Sortir des boucles. # "break N" sort de N niveaux de boucles. for boucleexterne in 1 2 3 4 5 do echo -n "Groupe $boucleexterne: " # ---------------------------------------------------------- for boucleinterne in 1 2 3 4 5 do echo -n "$boucleinterne " if [ "$boucleinterne" -eq 3 ] then break # Essayez break 2 pour voir ce qui se passe. # (Sort des boucles internes et externes.) fi done # ---------------------------------------------------------- echo done echo exit 0
La commande continue, similaire à break, prend un paramètre de façon optionnelle. Un simple continue court-circuite l'itération courante et commence la prochaine itération de la boucle dans laquelle elle se trouve. Un continue N termine toutes les itérations à partir de son niveau de boucle et continue avec l'itération de la boucle N niveaux au-dessus.
Exemple 11.22. Continuer à un plus haut niveau de boucle
#!/bin/bash # La commande "continue N" continue jusqu'au niveau de boucle N. for exterieur in I II III IV V # Boucle extérieure do echo; echo -n "Groupe $exterieur : " # ---------------------------------------------------------- for interieur in 1 2 3 4 5 6 7 8 9 10 # Boucle intérieure do if [ "$interieur" -eq 7 ] then continue 2 # Continue la boucle au deuxième niveau, c'est-à-dire la #+ boucle extérieure. # Remplacez la ligne ci-dessus avec un simple "continue" # pour voir le comportement normal de la boucle. fi echo -n "$interieur " # 7 8 9 10 ne s'afficheront jamais. done # ---------------------------------------------------------- done echo; echo # Exercice : # Parvenir à un emploi utile pour "continue N" dans un script. exit 0
Exemple 11.23. Utiliser continue N dans une tâche courante
# Albert Reiner donne un exemple pour l'utilisation de "continue N" : # ------------------------------------------------------------------- # Supposez que j'ai un grand nombre de jobs à exécuter, avec des données à #+ traiter dans des fichiers dont le nom correspond à un certain modèle #+ et qui font tous partie d'un même répertoire. #+ Plusieurs machines accèdent à ce répertoire et je veux distribuer le #+ travail entre ces différentes machines. Alors, j'exécute ce qui suit #+ avec nohup sur toutes les machines : while true do for n in .iso.* do [ "$n" = ".iso.opts" ] && continue beta=${n#.iso.} [ -r .Iso.$beta ] && continue [ -r .lock.$beta ] && sleep 10 && continue lockfile -r0 .lock.$beta || continue echo -n "$beta: " `date` run-isotherm $beta date ls -alF .Iso.$beta [ -r .Iso.$beta ] && rm -f .lock.$beta continue 2 done break done # Les détails, en particulier le sleep N, sont spécifiques à mon #+ application mais le modèle général est : while true do for job in {modèle} do {job déjà terminé ou en cours d'exécution} && continue {indiquez que ce job est en cours d'exécution, exécutez le job, indiquez-le comme terminé} continue 2 done break # Ou quelque chose comme `sleep 600' pour éviter la fin. done # De cette façon, le script s'arrêtera seulement quand il n'y aura plus de jobs #+ à faire (en incluant les jobs qui ont été ajoutés à l'exécution). À travers # l'utilisation de fichiers verrous appropriés, il peut être exécuté sur #+ plusieurs machines en même temps sans duplication des calculs [qui ont #+ demandé quelques heures dans mon cas, donc je veux vraiment éviter ceci]. #+ De plus, comme la recherche recommence toujours au début, vous pouvez coder des priorités dans les noms des fichiers. Bien sûr, vous pouvez le #+ faire sans `continue 2' mais alors vous devrez vérifier réellement si #+ un job s'est terminé (pour rechercher immédiatement le prochain #+ job) ou non (auquel cas nous arrêtons le programme ou l'endormissons #+ pour un long moment le temps que vous cherchions un autre job)..
L'expression continue N est difficile à comprendre et complexe à utiliser dans tous les contextes. Il est probablement raisonnable de l'éviter.
[52] Ce sont des commandes intégrées du shell, alors que les autres commandes de boucle, telles que while et case, sont des mots clés.