168 votes

Emuler une boucle do-while en Bash

Quelle est la meilleure façon d'émuler une boucle do-while en Bash ?

Je pourrais vérifier la condition avant d'entrer dans le while et ensuite continuer à revérifier la condition dans la boucle, mais c'est du code en double. Existe-t-il un moyen plus propre ?

Pseudo code de mon script :

while [ current_time <= $cutoff ]; do
    check_if_file_present
    #do other stuff
done

Cela ne fonctionne pas check_if_file_present si elle est lancée après la $cutoff temps, et un do-while le ferait.

0 votes

Vous cherchez le until changer ?

2 votes

@MichaelGardner until évaluera également la condition avant d'exécuter le corps de la boucle

2 votes

Ah, je vois, j'ai mal compris votre question.

260voto

jm666 Points 17312

Deux solutions simples :

  1. Exécutez votre code une fois avant la boucle while

    actions() {
       check_if_file_present
       # Do other stuff
    }
    
    actions #1st execution
    while [ current_time <= $cutoff ]; do
       actions # Loop execution
    done
  2. Ou :

    while : ; do
        actions
        [[ current_time <= $cutoff ]] || break
    done

15 votes

: est un élément incorporé, équivalent à l'élément incorporé true . Tous deux "ne font rien avec succès".

2 votes

@loxaxs c'est vrai dans zsh par exemple mais pas dans Bash. true est un programme réel alors que : est intégré. Le premier se termine simplement par 0 (et false con 1 ), ce dernier ne fait absolument rien. Vous pouvez vérifier avec which true .

0 votes

@Fleshgrinder : est toujours utilisable à la place de true dans Bash. Essayez-le avec while :; do echo 1; done .

158voto

Dennis Williamson Points 105818

Placez le corps de votre boucle après le while et avant le test. Le corps même de la while devrait être un non-opérationnel.

while 
    check_if_file_present
    #do other stuff
    (( current_time <= cutoff ))
do
    :
done

Au lieu des deux points, vous pouvez utiliser continue si vous trouvez cela plus lisible. Vous pouvez également insérer une commande qui lancera uniquement entre itérations (pas avant la première ou après la dernière), comme par exemple echo "Retrying in five seconds"; sleep 5 . Ou imprimez des délimiteurs entre les valeurs :

i=1; while printf '%d' "$((i++))"; (( i <= 4)); do printf ','; done; printf '\n'

J'ai modifié le test pour utiliser des doubles parenthèses puisque vous semblez comparer des entiers. À l'intérieur des doubles parenthèses, les opérateurs de comparaison tels que <= sont lexicales et donneront un résultat erroné lorsqu'on compare 2 et 10, par exemple. Ces opérateurs ne fonctionnent pas à l'intérieur de crochets simples.

0 votes

Est-il équivalent à une ligne unique while { check_if_file_present; ((current_time<=cutoff)); }; do :; done ? C'est-à-dire que les commandes à l'intérieur de la section while condition effectivement séparée par des points-virgules et non par des ex. && et regroupés par {} ?

0 votes

@Ruslan : Les accolades ne sont pas nécessaires. Vous ne devriez pas lier quoi que ce soit au test à l'intérieur des doubles parenthèses en utilisant && o || puisque cela les fait effectivement faire partie du test qui contrôle la while . À moins que vous n'utilisiez cette construction sur la ligne de commande, je ne la ferais pas en une ligne (dans un script, spécifiquement) car l'intention est illisible.

0 votes

Oui, je n'avais pas l'intention de l'utiliser comme une ligne unique : juste pour clarifier comment les commandes du test sont connectées. Je craignais que la première commande renvoyant un résultat différent de zéro ne rende la condition entière fausse.

6voto

KJ7LNW Points 193

Cette mise en œuvre :

  • Pas de duplication de code
  • Ne nécessite pas de fonctions supplémentaires()
  • Ne dépend pas de la valeur de retour du code dans la section "while" de la boucle :

    do=true while $do || conditions; do do=false

    your code ...

    done

Cela fonctionne aussi avec une boucle de lecture, en sautant la première lecture :

do=true
while $do || read foo; do
  do=false

  # your code ...
  echo $foo
done

4voto

Chetabahana Points 3161

Nous pouvons émuler une boucle do-while en Bash avec while [[condition]]; do true; done comme ça :

while [[ current_time <= $cutoff ]]
    check_if_file_present
    #do other stuff
do true; done

Pour un exemple. Voici ma mise en œuvre pour obtenir connexion ssh dans le bash script :

#!/bin/bash
while [[ $STATUS != 0 ]]
    ssh-add -l &>/dev/null; STATUS="$?"
    if [[ $STATUS == 127 ]]; then echo "ssh not instaled" && exit 0;
    elif [[ $STATUS == 2 ]]; then echo "running ssh-agent.." && eval `ssh-agent` > /dev/null;
    elif [[ $STATUS == 1 ]]; then echo "get session identity.." && expect $HOME/agent &> /dev/null;
    else ssh-add -l && git submodule update --init --recursive --remote --merge && return 0; fi
do true; done

Il donnera la sortie en séquence comme ci-dessous :

Step #0 - "gcloud": intalling expect..
Step #0 - "gcloud": running ssh-agent..
Step #0 - "gcloud": get session identity..
Step #0 - "gcloud": 4096 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /builder/home/.ssh/id_rsa (RSA)
Step #0 - "gcloud": Submodule '.google/cloud/compute/home/chetabahana/.docker/compose' (git@github.com:chetabahana/compose) registered for path '.google/cloud/compute/home/chetabahana/.docker/compose'
Step #0 - "gcloud": Cloning into '/workspace/.io/.google/cloud/compute/home/chetabahana/.docker/compose'...
Step #0 - "gcloud": Warning: Permanently added the RSA host key for IP address 'XXX.XX.XXX.XXX' to the list of known hosts.
Step #0 - "gcloud": Submodule path '.google/cloud/compute/home/chetabahana/.docker/compose': checked out '24a28a7a306a671bbc430aa27b83c09cc5f1c62d'
Finished Step #0 - "gcloud"

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