Maîtriser les conditions en bash

Quentin Busuttil

Le scripting bash est souvent déroutant. La syntaxe est parfois assez éloignée de ce à quoi nous sommes habitués dans d'autres langages. C'est d'ailleurs pourquoi beaucoup l'évitent au maximum. Pourtant, tôt ou tard, on a tous besoin d'écrire un petit .sh pour gérer un service ou automatiser un comportement sur un serveur.

Voilà pourquoi un petit rappel du fonctionnement des conditions - base de tout langage de programmation - en bash peut s'avérer salvateur. En route pour bashland !

Sous Linux et Unix, il existe plusieurs interpréteurs de commandes ou shells. Les fonctions supportées par l'un ou l'autre peuvent varier. Ainsi, nous parlons ici de bash, alias bourne again shell, une implémentation du shell standard d'Unix, sh, le shell historique. Bash est 100 % compatible avec sh, en revanche, la réciproque n'est pas vraie. Nous verrons justement cela dans les conditions.

Pour réagir au contenu de ce tutoriel, un espace de dialogue vous est proposé sur le forum. Commentez

- Titre : Maîtriser les conditions en bash

- Auteur : Quentin Busuttil

- Parution : 25 septembre 2017

- Mise à jour : 21 novembre 2017

- Licence : Le contenu de cet article est rédigé par Quentin Busuttil et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Pas de Modification 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- I. Anatomie d'un if

- II. Les syntaxes conditionnelles

- II-A. Conditions à simples crochets

- II-A-1. Conditions sur les fichiers

- II-A-2. Conditions sur les strings

- II-A-3. Conditions arithmétiques

- II-B. Conditions à doubles crochets

- II-C. La syntaxe doubles parenthèses

- III. Table des conditions

- III-A. Conditions sur les fichiers

- III-B. Conditions sur les chaînes de caractères

- III-C. Conditions sur les entiers

- IV. Note de la rédaction de Developpez.com

I - Anatomie d'un if

Prenons les choses dans l'ordre. En bash, point d'indentation ou d'accolades, un if se démarque par des mots clefs de début et de fin. Par ailleurs, on trouvera des crochets en lieu et place des habituelles parenthèses.

1. # un si simple

2. if [ test ]

3. then

4. echo "le test est passé"

5. fi # on ferme toujours la condition par fi (if en verlan !)

7. # et avec un else if et un else

8. if [ test ]

9. then

10. echo "le test est passé"

11. elif [ test2 ]

12. echo "le test2 est passé"

13. else

14. echo "les deux tests ont échoué"

15. fi

Évidemment, vous pouvez tout à fait omettre le elif, le else ou les deux. Vous pouvez également mettre plusieurs elif les uns à la suite des autres.

Vous noterez bien que la condition est toujours entourée d'un espace après le crochet d'ouverture et avant le crochet de fermeture.

Dans les exemples ci-dessus, chaque mot clef est sur sa propre ligne. Il est néanmoins possible de placer le then sur la même ligne que la condition en utilisant un point-virgule.

if [ test ]; then
echo "le test est passé"
fi

II - Les syntaxes conditionnelles

Nous l'avons dit en introduction, bash est une évolution de sh. À ce titre, il comprend les conditions classiques de sh, mais il intègre aussi une syntaxe plus avancée, laquelle n'est pas rétrocompatible. Voyons tout cela.

II-A - Conditions à simples crochets

Ce sont les conditions compatibles avec sh. Elles fonctionnent très bien dans la majorité des cas. On dénombre trois grandes familles de conditions : les conditions basées sur des fichiers ou dossiers, celles basées sur des chaînes de caractères et enfin celles concernant des valeurs arithmétiques.

II-A-1 - Conditions sur les fichiers

Permet de vérifier si un fichier ou un répertoire existe, sa nature, etc. Nous verrons les différentes valeurs de conditions dans la suite de l'article.

1. if [ -f ceci_est_un_fichier ]

2. then

3. echo "il s'agit bien d'un fichier"

4. fi

II-A-2 - Conditions sur les strings

Comme dans tout programme qui se respecte, on a toujours à traiter des chaînes de caractères.

1. if [ -z "$variable_vide" ]

2. then

3. echo "la variable est bien vide"

4. fi

6. # comparaison

7. if [ "$variable" == "ta mère" ]

8. then

9. echo "la variable contient la chaîne ta mère"

10. fi

Vous remarquez peut-être que la variable est entourée de guillemets. Bien que ce ne soit pas une obligation, ça vous évitera des bogues si la variable contient des espaces ou des retours à la ligne par exemple.

II-A-3 - Conditions arithmétiques

On va ici comparer des entiers. Supérieur, inférieur, égal… la base quoi.

1. if [ $num -lt 1 ]

2. then

3. echo "la variable est inférieure à 1"

4. fi

6. if [ $num -eq 100 ]

7. then

8. echo "la variable est égale à 100"

9. fi

II-B - Conditions à doubles crochets

Ces conditions permettent tout ce qu'offrent les conditions à simples crochets et plus. En revanche, elles ne sont pas compatibles avec sh. Elles prennent la forme suivante :

1. if [[ -z "$variable_vide" ]]

2. then

3. echo "la variable est bien vide"

4. fi

Ces conditions améliorées proposent l'usage du wildcard comme en bash ainsi que des expressions régulières. Ainsi, il est possible d'avoir des conditions comme cela :

1. if [[ "$variable" ==  *superman* ]]

2. then

3. echo "la variable contient le mot superman"

4. fi

6. if [[ "$variable" ==  superman* ]]

7. then

8. echo "la variable commence par le mot superman"

9. fi

11. if [[ "$variable" ==  [sS]uperman* ]]

12. then

13. echo "la variable commence par le mot superman ou Superman"

14. fi

16. if [[ "$email" =~ "b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,4}b" ]]

17. then

18. echo "la variable contient un email valide"

19. fi

On a donc un simili d'expression régulière, ce qui est assez confortable. En revanche, le caractère « * » n'est pas interprété dans les conditions sur les fichiers.

1. if [[ -a *.sh ]]

2. then

3. echo "le fichier *.sh existe"

4. fi

6. # la syntaxe classique recherche un fichier en .sh, n'importe lequel

7. if [ -a *.sh ]

8. then

9. echo "un fichier en *.sh existe"

10. fi

Avec la syntaxe classique, la condition vaut true s'il y a un fichier en .sh, false s'il n'y en a pas, mais une erreur est retournée s'il y en a plusieurs…

La syntaxe améliorée permet aussi l'usage des opérateurs classiques de conditions tels que || et &&. Par ailleurs, il est possible d'omettre les guillemets autour des variables, car les espaces ne causent plus problème. C'est cependant une bonne habitude à conserver, car ailleurs dans les scripts, il est plus prudent de les mettre.

II-C - La syntaxe doubles parenthèses

Cette syntaxe dédiée aux comparaisons arithmétiques peut sembler plus familière, car elle autorise les opérateurs plus connus dans d'autres langages : ==, !=, < et >. Elle supporte aussi les opérateurs && et ||.

1. if (( $a != 0 ))

2. then

3. echo "$a est non nul"

4. fi

III - Table des conditions

On termine cet article en passant en revue les conditions les plus utiles. Pour un aperçu exhaustif de toutes les possibilités, je vous laisse vous référer à The Linux Documentation Project.

III-A - Conditions sur les fichiers

Condition

Vrai si

Détails

[ -a fichier ]

« fichier » existe et est un fichier.

-

[ -b blockspecialfile ]

Le fichier « blockspecialfile » existe et est de type bloc spécial.

Les « block special files » (fichier spécial en mode bloc) sont des fichiers qui représentent des périphériques, souvent des disques durs ou clefs USB. On les trouve principalement dans /dev ex. /dev/sda (disque a) et /dev/sda1 (partition 1 du disque a).

[ -c characterspecialfile ]

Le fichier « characterspecialfile » existe et est de type caractère spécial.

Les « character special files » (fichier spécial en mode caractère) sont des fichiers spéciaux du kernel, différents des « block special files ». Ils ne sont pas mis en mémoire tempon, buffered, leur effet est immédiat : envoi du caractère au driver correspondant (émission d'un son via la carte son, etc.). Ce type de fichier concerne aussi les pseudo-périphériques (pseudo-devices)
/dev/null, /dev/zero, /dev/full, /dev/random et /dev/urandom. N'hésitez pas à jeter un œil à ce post Stackexchange pour plus de détails sur ces fichiers spéciaux.

[ -d dossier ]

dossier existe et est un dossier.

-

[ -e fichier ]

Même comportement que -a.

-

[ -f fichier_normal ]

« fichier_normal » existe et est un fichier « normal ».

Par normal, on entend que le fichier n'est ni de type bloc spécial, ni de type caractère spécial.

[ -h lien_symbolique ]

« lien_symbolique » existe et est un lien symbolique.

-

[ -L lien_symbolique ]

Même comportement que -h.

[ -r fichier_lisible ]

« fichier_lisible existe » et est lisible par le script.

-

[ -s fichier_non_vide ]

« fichier_non_vide » existe et possède une taille non nulle.

-

[ -w fichier_writable ]

« fichier_writable » existe et le script peut y écrire.

-

[ -x executable ]

« executable » existe et est exécutable depuis le script.

Concernant un répertoire, le droit d'exécution permet simplement de lister son contenu.

III-B - Conditions sur les chaînes de caractères

Nous avons vu le plus complexe. En effet, Linux possède de nombreux types de fichiers et nous avons donc des conditions qui n'ont pas lieu d'exister dans tous les langages.

Les conditions sur les chaînes de caractères sont plus classiques. Les == et != se passent d'explications. < et > permettent de déterminer si une chaîne est avant un autre dans un classement par ordre alphabétique… oui ça peut servir, who knows.

Enfin, deux conditions un peu particulières permettent de savoir si une chaîne de caractères est vide ou non : [ -n string_non_vide ] et [ -z string_vide ].

III-C - Conditions sur les entiers

Nous l'avons vu, avec la syntaxe à doubles parenthèses, rien de sorcier, c'est comme nous en avons l'habitude dans tous les autres langages. En revanche, pour la syntaxe classique, avec les crochets, c'est un peu moins intuitif. Si vous connaissez MongoDB, c'est comme les conditions en Mongo.

- [ nombre_1 -eq nombre_2 ] pour equal, égal.

- [ nombre_1 -ne nombre_2 ] pour not equal, non égal.

- [ nombre_1 -gt nombre_2 ] pour greater than, plus grand que.

- [ nombre_1 -ge nombre_2 ] pour greater than or equal, plus grand ou égal.

- [ nombre_1 -lt nombre_2 ] pour less than, plus petit.

- [ nombre_1 -le nombre_2 ] pour less than or equal.

Et voilà, nous avons là un bon condensé des possibilités offertes par les conditions en bash - et en shell sh. N'hésitez pas à apporter des précisions/corrections ou à poser des questions en commentaires. Il ne vous reste plus qu'à coder. 3… 2… 1… #!/bin/bash !

IV - Note de la rédaction de Developpez.com

Nous tenons à remercier Quentin Busuttil qui nous a aimablement autorisés à publier son tutoriel : Maitriser les conditions en bash. Nous remercions également Winjerome pour la mise au gabarit et Claude Leloup pour la relecture orthographique.