Maitriser les conditions en bash

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 shell. Les fonctions supportées par l’un ou l’autre peuvent varier. Ainsi, nous parlons ici de bash, alias bourne again shell, le successeur de sh, le shell historique. Ce dernier est 100% compatible avec sh, en revanche, la réciproque n’est pas vraie. Nous verrons justement cela dans les conditions.

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.

# un si simple
if [ test ]
then
  echo "le teste est passé"
fi # on ferme toujours la condition par fi (if en verlan !)

# et avec un else if et un else
if [ test ]
then
  echo "le teste est passé"
elif [ test2 ]
  echo "le test2 est passé"
else
  echo "les deux tests ont échoué"
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 teste est passé"
fi

Les syntaxes conditionnelles

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

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 dossier, celles basées sur des chaînes de caractères et enfin celles concernant des valeurs arithmétiques.

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.

if [ -f ceci_est_un_fichier ]
then
  echo "il s'agit bien d'un fichier"
fi

Conditions sur les strings

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

if [ -z "$variable_vide" ]
then
  echo "la variable est bien vide"
fi

# comparaison
if [ "$variable" == "ta mère" ]
then
  echo "la variable contient la chaîne ta mère"
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 bugs si la variable contient des espaces ou des retours à la ligne par exemple.

Conditions arithmétiques

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

if [ $num -lt 1 ]
then
  echo "la variable est inférieure à 1"
fi

if [ $num -eq 100 ]
then
  echo "la variable est égale à 100"
fi

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.

if [[ -z "$variable_vide" ]]
then
  echo "la variable est bien vide"
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 :

if [[ "$variable" ==  *superman* ]]
then
  echo "la variable contient le mot superman"
fi

if [[ "$variable" ==  superman* ]]
then
  echo "la variable commence par le mot superman"
fi

if [[ "$variable" ==  [sS]uperman* ]]
then
  echo "la variable commence par le mot superman ou Superman"
fi

if [[ "$email" =~ "b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,4}b" ]]
then
  echo "la variable contient un email valide"
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.

if [[ -a *.sh ]]
then
  echo "le fichier *.sh existe"
fi

# la syntaxe classique recherche un fichier en .sh, n'importe lequel
if [ -a *.sh ]
then
  echo "un fichier en *.sh existe"
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é 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 pausent plus problème. C’est cependant une bonne habitude à conserver car ailleurs dans les scripts, il est plus prudent de les mettre.

La syntaxe double 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 ||.

if (( $a != 0 ))
then
  echo "$a est non nul"
fi

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.

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 block special. Les block special files sont des fichiers qui représentent des périphériques, souvent des disques dur 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. Character special files sont des fichiers spéciaux du kernel, différents des block special files, ils ne sont pas mis en mémoire tampon, buffered, leur effet est immédiat : envoie du caractère au driver correspondant (émission d'un son via la carte son etc). Ce type de fichier concerne aussi les 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 block, ni character special.
[ -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 du 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.

Conditions sur les strings

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étermine si un string est avant un autre dans un classement par ordre alphabétique… oui ça peut servir, who knows.

Enfin, deux conditions un peu particulière permettent de savoir si un string est vide ou non : [ -n string_non_vide ] et [ -z string_vide ].

Conditions sur les entiers

Nous l'avons vu, avec la syntaxe à double 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 pricisions/corrections ou à poser des questions en commentaires. Il ne vous reste plus qu'à coder. 3… 2… 1… #!/bin/bash !

Déjà 2 réponses, rejoignez la discussion !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *