Comment valider la syntaxe d’un script Linux Bash avant de l’exécuter

fatmawati achmad zaenuri/Shutterstock

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 façons de vérifier la syntaxe de vos scripts avant même de les exécuter.

Ces insectes embêtants

É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 cela. 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 avez de travail à faire vous-même. Python peut profiter des routines intégrées de récupération de place, mais le C et l’assembly ne le font certainement pas.

L’écriture de scripts shell Linux pose ses propres défis. Avec un langage compilé comme 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 du code machine que l’ordinateur peut comprendre et agir.

Le compilateur ne générera un fichier binaire que si le code source qu’il lit et analyse respecte la syntaxe et les autres règles du langage. Si vous épelez un mot reservé—un des mots de commande du langage—ou un nom de variable incorrect, le compilateur renverra une erreur.

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

Les bugs dus à défauts logiques sont généralement beaucoup plus difficiles à repérer. Si vous dites à votre programme d’ajouter deux et trois mais que vous vouliez vraiment qu’il ajoute 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 rien de mal avec 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.

Les tests sont difficiles

Tester minutieusement un programme, même simple, prend du temps. L’exécuter plusieurs fois n’est pas suffisant ; vous devez vraiment tester tous les chemins d’exécution dans votre code, afin que toutes les parties du code soient vérifiées. Si le programme demande une entrée, vous devez fournir une plage 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 de savoir s’il existe des outils que nous pouvons utiliser pour nous aider à écrire des scripts shell Bash sans bogue.

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

Utilisation de Bash pour vérifier la syntaxe du script

La fête -n (noexec) indique à Bash de lire un script et de vérifier les erreurs de syntaxe, sans exécuter le script. Selon ce que votre script est destiné à faire, cela peut être beaucoup plus sûr que de l’exécuter et de rechercher des problèmes.

Voici le script que nous allons vérifier. Ce n’est pas compliqué, c’est principalement un ensemble de if déclarations. Il demande et accepte un nombre représentant un mois. Le script décide à quelle saison appartient le mois. É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 "Enter a month (1 to 12): " month

# did they enter anything?
if [ -z "$month" ]
then
  echo "You must enter a number representing a month."
  exit 1
fi

# is it a valid month?
if (( "$month" < 1 || "$month" > 12)); then
  echo "The month must be a number between 1 and 12."
  exit 0
fi

# is it a Spring month?
if (( "$month" >= 3 && "$month" < 6)); then
  echo "That's a Spring month."
  exit 0
fi

# is it a Summer month?
if (( "$month" >= 6 && "$month" < 9)); then
  echo "That's a Summer month."
  exit 0
fi

# is it an Autumn month?
if (( "$month" >= 9 && "$month" < 12)); then
  echo "That's an Autumn month."
  exit 0
fi

# it must be a Winter month
echo "That's a Winter month."
exit 0

Cette section vérifie si l’utilisateur a saisi quoi que ce soit. Il teste si le $month la variable n’est pas définie.

if [ -z "$month" ]
then
  echo "You must enter a number representing a month."
  exit 1
fi

Cette section vérifie s’ils ont saisi un nombre compris entre 1 et 12. Elle intercepte également une entrée non valide qui n’est pas un chiffre, car les lettres et les symboles de ponctuation ne se traduisent pas en valeurs numériques.

# is it a valid month?
if (( "$month" < 1 || "$month" > 12)); then
  echo "The month must be a number between 1 and 12."
  exit 0
fi

Toutes les autres clauses If vérifient si la valeur dans $month variable est 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é.

# is it a Summer month?
if (( "$month" >= 6 && "$month" < 9)); then
  echo "That's a Summer month."
  exit 0
fi

Si vous souhaitez travailler sur nos exemples, copiez et collez le texte du script dans un éditeur et enregistrez-le sous « seasons.sh ». Ensuite, rendez le script exécutable en utilisant le chmod commander:

chmod +x seasons.sh

Définition de l'autorisation exécutable sur un script

Nous pouvons tester le script en

  • Ne fournissant aucune contribution.
  • Fournir une entrée non numérique.
  • Fournir une valeur numérique qui est en dehors de la plage de 1 à 12.
  • Fournir des valeurs numériques comprises entre 1 et 12.

Dans tous les cas, on démarre le script avec la même commande. La seule différence est l’entrée que l’utilisateur fournit lorsqu’il est promu par le script.

./seasons.sh

Tester un script avec une variété d'entrées valides et non valides

Cela semble fonctionner comme prévu. Laissons Bash vérifier la syntaxe de notre script. Nous le faisons en invoquant le -n (noexec) et en passant le nom de notre script.

bash -n ./seasons.sh

Utiliser Bash pour tester la syntaxe d'un script

C’est un cas de « pas de nouvelles, bonnes nouvelles ». Nous renvoyer silencieusement à l’invite de commande est la manière de Bash de dire que tout semble OK. Sabotons notre script et introduisons une erreur.

Nous supprimerons le then Depuis le premier if clause.

# is it a valid month?
if (( "$month" < 1 || "$month" > 12)); # "then" has been removed
  echo "The month must be a number between 1 and 12."
  exit 0
fi

Exécutons maintenant le script, d’abord sans puis avec l’entrée de l’utilisateur.

./seasons.sh

Tester 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 toutes les autres lignes de code – car il ne se soucie pas de la logique du scénario. L’utilisateur n’est pas invité à entrer un nombre lorsque Bash vérifie le script, car le script 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 haut du script vers le bas, en vérifiant la syntaxe pour chaque ligne.

L’utilitaire ShellCheck

Un linter – du nom d’un outil de vérification de code source C de l’apogée d’Unix – est un outil d’analyse de code utilisé pour détecter les erreurs de programmation, les erreurs de style et l’utilisation suspecte ou douteuse du langage. Les linters sont disponibles pour de nombreux langages de programmation et sont réputés pour être pédants. Tout ce qu’un linter trouve n’est pas un bogue en soimais tout ce qu’ils font porter à votre attention mérite probablement l’attention.

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

Mettons nos disparus then mot réservé dans notre script, et essayez autre chose. Nous allons retirer la parenthèse d’ouverture « [” from the very first if clause.

# did they enter anything?
if -z "$month" ] # parenthèse ouvrante "[" removed
then
  echo "You must enter a number representing a month."
  exit 1
fi

if we use Bash to check the script it doesn’t find a problem.

bash -n seasons.sh
./seasons.sh

An error message from a script that passed the syntax checking with no detected issues

But when we try to run the script we see an error message. And, despite the error message, the script continues to execute. This is why some bugs are so dangerous. If the actions taken further on in the script rely on valid input from the user, the script’s behavior will be unpredictable. It could potentially put data at risk.

The reason the Bash -n (noexec) option doesn’t find the error in the script is the opening bracket “[” is an external program called [. It isn’t part of Bash. It is a shorthand way of using the test command.

Bash doesn’t check the use of external programs when it is validating a script.

Installing ShellCheck

ShellCheck requires installation. To install it on Ubuntu, type:

sudo apt install shellcheck

Installing shellcheck on Ubuntu

To install ShellCheck on Fedora, use this command. Note that the package name is in mixed case, but when you issue the command in the terminal window it is all in lowercase.

sudo dnf install ShellCheck

Installing shellcheck on Fedora

On Manjaro and similar Arch-based distros, we use pacman:

sudo pacman -S shellcheck

Installing shellcheck on Manjaro

Using ShellCheck

Let’s try running ShellCheck on our script.

shellcheck seasons.sh

Checking a script with ShellCheck

ShellCheck finds the issue and reports it to us, and provides a set of links for further information. If you right-click a link and choose “Open Link” from the context menu that appears, the link will open in your browser.

ShellCheck reporting errors and warnings

ShellCheck also finds another issue, which isn’t as serious. It is reported in green text. This indicates it is a warning, not an out-and-out error.

Let’s correct our error and replace the missing “[.” One bug-fix strategy is to correct the highest priority issues first and work down to the lower priority issues like warnings later.

We replaced the missing “[” and ran ShellCheck once more.

shellcheck seasons.sh

Checking a script a second time with ShellCheck

The only output from ShellCheck refers to our previous warning, so that’s good. We have no high-priority issues needing fixing.

The warning tells us that using the read command without the -r (read as-is) option will cause any backslashes in the input to be treated as escape characters. This is a good example of the type of pedantic output a linter can generate. In our case the user shouldn’t be entering a backslash anyway—we need them to enter a number.

Warnings like this require a judgment call on the part of the programmer. Make the effort to fix it, or leave it as it is? It’s a simple two-second fix. And it’ll stop the warning cluttering up ShellCheck’s output, so we might as well take its advice. We’ll add an “r” to option the flags on the read command, and save the script.

read -pr "Enter a month (1 to 12): " month

Running ShellCheck once more gives us a clean bill of health.

No errors or warnings reported by ShellCheck

ShellCheck Is Your Friend

ShellCheck can detect, report, and advise on a whole range of issues. Check out their gallery of bad code, which shows how many types of problems it can detect.

It’s free, fast, and takes a lot of the pain out of writing shell scripts. What’s not to like?

Source-135