Les bogues et les fautes de frappe dans les scripts Linux Bash peuvent avoir des conséquences désastreuses lors de l’exécution du script. Voici quelques moyens de vérifier la syntaxe de vos scripts avant même de les exécuter.

Ces bogues encombrants

Écrire du code est difficile. Ou, pour être plus précis, écrire du code non trivial sans bogue est difficile. Et plus il y a de lignes de code dans un programme ou un script, plus il y a de chances qu’il y ait des bogues.

Le langage dans lequel vous programmez a une incidence directe sur ce point. La programmation en assembleur est beaucoup plus difficile que la programmation en C, et la programmation en C est plus difficile que la programmation en Python. Plus le langage dans lequel vous programmez est de bas niveau, plus vous devez travailler vous-même. Python peut bénéficier de routines de collecte de déchets intégrées, mais ce n’est certainement pas le cas du C et de l’assembleur.

L’écriture de scripts shell Linux pose ses propres défis. Avec un langage compilé comme le C, un programme appelé compilateur lit votre code source – les instructions lisibles par l’homme que vous tapez dans un fichier texte – et le transforme en un fichier exécutable binaire. Le fichier binaire contient les instructions en code machine que l’ordinateur peut comprendre et utiliser.

Le compilateur ne génère un fichier binaire que si le code source qu’il lit et analyse obéit à la syntaxe et aux autres règles du langage. Si vous épelez mal un mot réservé – un des mots de commande du langage – ou un nom de variable, le compilateur émettra une erreur.

Par exemple, certains langages insistent pour que vous déclariez une variable avant de l’utiliser, d’autres ne sont pas aussi pointilleux. Si le langage dans lequel vous travaillez vous demande de déclarer des variables mais que vous oubliez de le faire, le compilateur affichera un message d’erreur différent. Aussi ennuyeuses que soient ces erreurs de compilation, elles permettent de détecter de nombreux problèmes et vous obligent à les résoudre. Mais même si vous avez un programme qui n’a pas de bogues syntaxiques, cela ne signifie pas qu’il n’y a pas de bogues dans ce programme. Loin de là.

Les bogues qui sont dus à des défauts logiques sont généralement beaucoup plus difficiles à repérer. Si vous demandez à votre programme d’additionner deux et trois alors que vous vouliez vraiment qu’il additionne deux et deux, vous n’obtiendrez pas la réponse que vous attendiez. Mais le programme fait ce pour quoi il a été écrit. Il n’y a aucun problème dans la composition ou la syntaxe du programme. Le problème, c’est vous. Vous avez écrit un programme bien formé qui ne fait pas ce que vous vouliez.

COMMENT UTILISER SET ET PIPEFAIL DANS LES SCRIPTS BASH SOUS LINUX

Les tests sont difficiles

Tester minutieusement un programme, même simple, prend beaucoup de temps. Il ne suffit pas de l’exécuter plusieurs fois ; vous devez vraiment tester tous les chemins d’exécution de votre code, afin que toutes les parties du code soient vérifiées. Si le programme demande des entrées, vous devez fournir une gamme suffisante de valeurs d’entrée pour tester toutes les conditions, y compris les entrées inacceptables.

Pour les langages de niveau supérieur, les tests unitaires et les tests automatisés permettent de faire des tests approfondis un exercice gérable. La question est donc la suivante : existe-t-il des outils qui peuvent nous aider à écrire des scripts shell Bash sans bogues ?

La réponse est oui, y compris le shell Bash lui-même.

Utilisation de Bash pour vérifier la syntaxe des scripts

L’option -n (noexec) de Bash indique à Bash de lire un script et de vérifier qu’il ne contient pas d’erreurs de syntaxe, sans l’exécuter. En fonction de ce que votre script est censé faire, cela peut être beaucoup plus sûr que de l’exécuter et de rechercher les problèmes.

Voici le script que nous allons vérifier. Il n’est pas compliqué, il s’agit principalement d’un ensemble d’instructions if. Il demande, et accepte, un nombre représentant un mois. Le script décide de la saison à laquelle le mois appartient. Évidemment, cela ne fonctionnera pas si l’utilisateur ne fournit aucune entrée, ou s’il fournit une entrée invalide comme une lettre au lieu d’un chiffre.

! /bin/bash

read -p « Entrez un mois (1 à 12) :  » month

ont-ils saisi quelque chose ?

if [ -z « $month » ]
then
echo « Vous devez entrer un nombre représentant un mois ».
exit 1
fi

COMMENT UTILISER GETOPTS POUR ANALYSER LES OPTIONS DES SCRIPTS SHELL LINUX

s’agit-il d’un mois valide ?

if (( « $month » < 1 || « $month » > 12)) ; then
echo « Le mois doit être un nombre compris entre 1 et 12. »
exit 0
fi

s’agit-il d’un mois de printemps ?

if (( « $month » >= 3 && « $month » < 6)) ; then
echo « C’est un mois de printemps. »
exit 0
fi

est-ce un mois d’été ?

if (( « $month » >= 6 && « $month » < 9)) ; then
echo « C’est un mois d’été ».
exit 0
fi

est-ce un mois d’automne ?

if (( « $month » >= 9 && « $month » < 12)) ; then
echo « C’est un mois d’automne ».
exit 0
fi

il doit s’agir d’un mois d’hiver

echo « C’est un mois d’hiver. »
exit 0

Cette section vérifie si l’utilisateur a saisi quelque chose. Elle teste si la variable $month n’est pas définie.

if [ -z « $month » ]
then
echo « Vous devez entrer un nombre représentant un mois. »
exit 1
fi

Cette section vérifie s’ils ont saisi un nombre entre 1 et 12. Elle piège également les entrées invalides qui ne sont pas des chiffres, car les lettres et les symboles de ponctuation ne sont pas traduits en valeurs numériques.

s’agit-il d’un mois valide ?

if (( « $month » < 1 || « $month » > 12)) ; then
echo « Le mois doit être un nombre compris entre 1 et 12. »
exit 0
fi

Toutes les autres clauses If vérifient si la valeur de la variable $month est comprise entre deux valeurs. Si c’est le cas, le mois appartient à cette saison. Par exemple, si le mois saisi par l’utilisateur est 6, 7 ou 8, il s’agit d’un mois d’été.

Est-ce un mois d’été ?

if (( « $month » >= 6 && « $month » < 9)) ; then
echo « C’est un mois d’été ».
exit 0
fi

Si vous voulez travailler avec nos exemples, copiez et collez le texte du script dans un éditeur et enregistrez-le sous le nom de « seasons.sh ». Ensuite, rendez le script exécutable en utilisant la commande chmod :

chmod +x saisons.sh

Définition de l’autorisation d’exécution d’un script

Nous pouvons tester le script en

En ne fournissant aucune entrée.
Fournissant une entrée non numérique.
Fournissant une valeur numérique qui n'est pas comprise entre 1 et 12.
Fournissant des valeurs numériques comprises entre 1 et 12.

Dans tous les cas, nous lançons le script avec la même commande. La seule différence est l’entrée que l’utilisateur fournit lorsqu’il est encouragé par le script.

./seasons.sh

Test d’un script avec une variété d’entrées valides et invalides

Cela semble fonctionner comme prévu. Demandons à Bash de vérifier la syntaxe de notre script. Pour ce faire, nous invoquons l’option -n (noexec) et indiquons le nom de notre script.

bash -n ./seasons.sh

Utilisation de Bash pour tester la syntaxe d’un script

C’est un cas de « pas de nouvelles, bonnes nouvelles ». Le retour silencieux à l’invite de commande est la façon dont Bash nous dit que tout semble correct. Sabotons notre script et introduisons une erreur.

Nous allons supprimer le then de la première clause if.

est-ce un mois valide ?

if (( « $month » < 1 || « $month » > 12)) ; # « then » a été supprimé
echo « Le mois doit être un nombre entre 1 et 12 ».
exit 0
fi

Maintenant, exécutons le script, d’abord sans et ensuite avec l’entrée de l’utilisateur.

./seasons.sh

Test d’un script avec des entrées invalides et valides

La première fois que le script est exécuté, l’utilisateur n’entre pas de valeur et le script se termine donc. La section que nous avons sabotée n’est jamais atteinte. Le script se termine sans message d’erreur de Bash.

La deuxième fois que le script est exécuté, l’utilisateur fournit une valeur d’entrée, et la première clause if est exécutée pour vérifier l’intégrité de l’entrée de l’utilisateur. Cela déclenche le message d’erreur de Bash.

Notez que Bash vérifie la syntaxe de cette clause – et de chaque autre ligne de code – car il ne se soucie pas de la logique du script. L’utilisateur n’est pas invité à saisir un nombre lorsque Bash vérifie le script, car ce dernier n’est pas en cours d’exécution.

Les différents chemins d’exécution possibles du script n’affectent pas la façon dont Bash vérifie la syntaxe. Bash travaille simplement et méthodiquement du début à la fin du script, en vérifiant la syntaxe de chaque ligne.

L’utilitaire ShellCheck

Un linter – dont le nom provient d’un outil de vérification du code source C datant de l’âge d’or d’Unix – est un outil d’analyse du code utilisé pour détecter les erreurs de programmation, les erreurs stylistiques et l’utilisation suspecte ou douteuse du langage. Les linteurs sont disponibles pour de nombreux langages de programmation et sont réputés pour leur pédantisme. Tout ce qu’un linter trouve n’est pas un bogue en soi, mais tout ce qu’il vous signale mérite probablement votre attention.

ShellCheck est un outil d’analyse de code pour les scripts shell. Il se comporte comme un linter pour Bash.

Remettons notre mot réservé manquant then dans notre script, et essayons autre chose. Nous allons supprimer le crochet ouvrant « [ » de la toute première clause if.

ont-ils entré quelque chose ?

if -z « $month » ] # crochet ouvrant « [ » supprimé
puis
echo « Vous devez entrer un nombre représentant un mois. »
exit 1
fi

si nous utilisons Bash pour vérifier le script, il ne trouve pas de problème.

bash -n saisons.sh

./seasons.sh

Un message d’erreur d’un script qui a passé la vérification de la syntaxe sans problème.

Mais lorsque nous essayons d’exécuter le script, nous voyons un message d’erreur. Et, malgré le message d’erreur, le script continue à s’exécuter. C’est pourquoi certains bogues sont si dangereux. Si les actions entreprises plus loin dans le script dépendent d’une entrée valide de l’utilisateur, le comportement du script sera imprévisible. Il pourrait potentiellement mettre les données en danger.

La raison pour laquelle l’option -n (noexec) de Bash ne trouve pas l’erreur dans le script est que le crochet ouvrant « [ » est un programme externe appelé [. Il ne fait pas partie de Bash. C’est une façon abrégée d’utiliser la commande test.

Bash ne vérifie pas l’utilisation de programmes externes lorsqu’il valide un script.

Installation de ShellCheck

ShellCheck nécessite une installation. Pour l’installer sur Ubuntu, tapez :

sudo apt install shellcheck

Installation de shellcheck sur Ubuntu

Pour installer ShellCheck sur Fedora, utilisez cette commande. Notez que le nom du paquet est en majuscules et en minuscules, mais que lorsque vous lancez la commande dans la fenêtre du terminal, tout est en minuscules.

sudo dnf install ShellCheck

Installation de ShellCheck sur Fedora

Sur Manjaro et les distros similaires basées sur Arch, nous utilisons pacman :

sudo pacman -S shellcheck

Installation de shellcheck sur Manjaro

Utilisation de ShellCheck

Essayons d’exécuter ShellCheck sur notre script.

shellcheck seasons.sh

Vérification d’un script avec ShellCheck

ShellCheck trouve le problème, nous le signale et fournit une série de liens pour obtenir des informations complémentaires. Si vous cliquez avec le bouton droit de la souris sur un lien et choisissez « Ouvrir le lien » dans le menu contextuel qui apparaît, le lien s’ouvrira dans votre navigateur.

ShellCheck signale des erreurs et des avertissements

ShellCheck trouve également un autre problème, qui n’est pas aussi grave. Il est signalé en texte vert. Cela indique qu’il s’agit d’un avertissement et non d’une erreur flagrante.

Corrigeons notre erreur et remplaçons le « [. » manquant. Une stratégie de correction des bogues consiste à corriger d’abord les problèmes les plus prioritaires et à travailler ensuite sur les problèmes moins prioritaires comme les avertissements.

Nous avons remplacé le « [ » manquant et lancé ShellCheck une fois de plus.

shellcheck seasons.sh

Vérification d’un script une deuxième fois avec ShellCheck

Le seul résultat de ShellCheck fait référence à notre précédent avertissement, donc c’est bien. Nous n’avons aucun problème prioritaire à résoudre.

L’avertissement nous indique qu’en utilisant la commande read sans l’option -r (read as-is), les barres obliques inversées de l’entrée seront traitées comme des caractères d’échappement. Ceci est un bon exemple du type de sortie pédante qu’un linter peut générer. Dans notre cas, l’utilisateur ne devrait pas saisir de backslash de toute façon – nous avons besoin qu’il saisisse un nombre.

Les avertissements de ce type nécessitent un jugement de la part du programmeur. Faire l’effort de corriger le problème ou le laisser tel quel ? Il s’agit d’une simple correction de deux secondes. Et cela empêchera l’avertissement d’encombrer la sortie de ShellCheck, donc nous pourrions tout aussi bien suivre son conseil. Nous allons ajouter un « r » à l’option des drapeaux de la commande read, et enregistrer le script.

read -pr « Entrez un mois (1 à 12) :  » mois

L’exécution de ShellCheck une fois de plus nous donne un certificat de bonne santé.

Aucune erreur ou avertissement n’a été signalé par ShellCheck.

ShellCheck est votre ami

ShellCheck peut détecter, signaler et donner des conseils sur toute une série de problèmes. Jetez un coup d’œil à sa galerie de mauvais code, qui montre le nombre de types de problèmes qu’il peut détecter.

Il est gratuit, rapide et rend l’écriture de scripts shell beaucoup moins pénible. Qu’est-ce qu’on ne peut pas aimer ?