web-dev-qa-db-fra.com

Entre while et do dans le script Shell

J'ai le script suivant qui fonctionne bien:

!/bin/bash

a=12
while [ $a -gt 10 ]
do
    echo "$a"
    a=$(($a-1))
done
echo "done"

Si j'ajoute la ligne "echo something" au-dessus de "do", je m'attends à voir une erreur de syntaxe dans cette ligne. Il paraît que [ $a -gt 10 ] est contourné et devient une boucle infinie. Comment est-ce possible?

15
Geek

De le manuel bash :

while

La syntaxe de la commande while est:

while  test-commandes; faire  commandes-conséquentes; 
 fait
Exécutez consequent-commands aussi longtemps que test-commands a un état de sortie de zéro. Le statut de retour est le statut de sortie de la dernière commande exécutée dans consequent-commands, ou zéro si aucun n'a été exécuté.

Remarque: test-commands, pluriel. Vous pouvez utiliser plusieurs commandes dans le test, et c'est donc une boucle parfaitement valide, avec la liste des commandes [ $a -gt 10 ]; echo "$a" comme test:

while [ $a -gt 10 ]
echo "$a"
do
   a=$(($a-1))
done

Alors que la commande [ $a -gt 10 ] peut ou non échouer, le echo réussira (presque) toujours (à moins qu'il ne puisse pas écrire le texte, ou qu'une autre erreur se soit produite), donc le final l'état de sortie des commandes de test sera toujours réussi et la boucle sera toujours exécutée.

29
muru

De man bash:

tandis que list-1; faire liste-2; terminé
La commande while exécute en continu la liste list-2
tant que dernière commande dans la liste list-1
renvoie un état de sortie de zéro.

Ce qui implique qu'un list pourrait contenir plusieurs commandes, ce qu'il fait (séparées principalement par des points-virgules ou des sauts de ligne).

Donc, cela fonctionne parfaitement bien:

#!/bin/bash

a=12

while 
    echo something
    echo "a before test =  $a"
    [ a -gt 10 ]
do
    echo "a after test =  $a"
    a=$(($a-1))
done
echo "done"

Si la dernière commande avant do est en écho, le code de sortie que le do reçoit est toujours vrai (0) et la boucle devient infinie.

10
Isaac

Ce que les autres réponses ici impliquent mais ne disent pas explicitement, c'est que [ est une commande intégrée, pas une partie syntaxique de l'instruction while.

Essayez de taper help [ sur votre ligne de commande:

[: [ arg ... ]
Évaluez l'expression conditionnelle.

C'est un synonyme de la fonction intégrée "test", mais le dernier argument doit
être un littéral ], pour correspondre à l'ouverture [.

Votre script est donc exactement comme:

!/bin/bash
a=12
while test $a -gt 10
do
  echo "$a"
  a=$(($a-1))
done
echo "done"

Vous changez en:

!/bin/bash
a=12
while test $a -gt 10; echo something
do
  echo "$a"
  a=$(($a-1))
done
echo "done"
4
Paul Evans

Comme exemple de ceci étant poussé à l'extrême,/usr/bin/tzselect a généralement environ 70 lignes de code entre le while et le do de la boucle principale qui comprend des instructions case et la substitution de commande et une seule ligne entre le do et le done.

4
icarus

(en tant que note pour compléter d'autres réponses).

L'utilisation de plusieurs commandes dans la liste de conditions est souvent utilisée pour implémenter une boucle similaire à la do { blah; blah; } while (condition) de C, c'est-à-dire que la condition est vérifiée à la fin de la boucle afin que le code de la boucle soit exécuté au moins une fois.

Dans sh, vous le feriez comme:

while
  blah
  blah
  condition
do
  continue # or :
done

Bien que d'autres approches soient possibles comme:

while true; do
  blah
  blah
  condition || break
done

Ou:

continue=true
while "$continue"; do
  blah
  blah
  condition || continue=false
done
end=false
until "$end"; do
  blah
  blah
  condition || end=true
done
2
Stéphane Chazelas