Aller au contenu principal

Écrire un script fiable : bonnes pratiques

Objectifs de la séance

  • Comprendre pourquoi le quoting des variables est une question de sécurité, pas seulement de style
  • Utiliser set -eu pour rendre un script plus strict face aux erreurs
  • Structurer un script selon un plan reconnaissable (en-tête, options, constantes, fonctions, programme principal)
  • Distinguer sortie standard et sortie d'erreur, et utiliser des codes de sortie explicites
  • Découvrir shellcheck et le mode débogage bash -x
  • Faire le bilan des 6 séances précédentes de Bash niveau 1

Notions théoriques

Le quoting : la règle numéro 1

Nous avons déjà recommandé, séance après séance, d'entourer vos variables de guillemets doubles. Voici pourquoi c'est réellement important, et pas une simple habitude esthétique.

danger

Imaginez ce script :

rm -rf $dossier/*

Si la variable dossier n'a jamais été définie (faute de frappe dans son nom, argument manquant...), elle est vide. La commande devient alors rm -rf /*, qui tente de supprimer l'intégralité du système de fichiers.

Avec des guillemets, rm -rf "$dossier"/*, une variable vide donne rm -rf "/*" — toujours risqué, mais le comportement reste au moins prévisible et le problème est plus facile à anticiper avec les vérifications vues en séance 4.

Le quoting protège aussi contre les espaces et les caractères spéciaux dans les valeurs de vos variables, comme nous l'avons vu dès la séance 2.

set -e et set -u

Deux options, à placer juste après le shebang, rendent un script bien plus strict :

#!/bin/bash
set -eu
  • set -e : le script s'arrête immédiatement dès qu'une commande échoue (code de retour différent de 0), au lieu de continuer comme si de rien n'était.
  • set -u : le script s'arrête si vous utilisez une variable qui n'a jamais été définie — cela aurait évité l'exemple catastrophique ci-dessus.
info

Vous croiserez aussi set -o pipefail, qui améliore la détection d'erreur dans une chaîne de commandes reliées par des tubes (|). Nous l'aborderons au niveau 2, avec les pipelines.

attention

set -e ne remplace pas les vérifications explicites vues en séance 4 : certaines commandes (dans un if, par exemple) ont le droit d'échouer sans arrêter le script, c'est même tout l'intérêt d'un test. set -e est un filet de sécurité pour les échecs inattendus, pas un substitut à une gestion d'erreur réfléchie.

La structure canonique d'un script propre

À ce stade du cours, un bon script d'administration suit un plan reconnaissable :

  1. Shebang (#!/bin/bash)
  2. En-tête commentée (rôle, auteur)
  3. set -eu
  4. Constantes
  5. Fonctions
  6. Programme principal
  7. exit 0 final explicite

Des codes de sortie cohérents

Il est utile d'adopter une convention personnelle, par exemple :

  • 0 : tout s'est bien passé
  • 1 : erreur d'usage (mauvais arguments)
  • 2 : erreur pendant l'exécution (commande qui a échoué)

Un script appelant un autre script peut ainsi réagir différemment selon le code reçu.

Sortie standard et sortie d'erreur

Toute commande Linux dispose de deux canaux de sortie distincts : la sortie standard (le résultat normal) et la sortie d'erreur (les messages de diagnostic). Rediriger un message vers la sortie d'erreur se fait avec >&2 :

echo "Erreur : dossier introuvable" >&2

Cela permet de séparer, par exemple avec ./script.sh > resultat.txt 2> erreurs.txt, ce qui est un résultat exploitable de ce qui est un message de diagnostic.

Déboguer un script

  • bash -x script.sh exécute le script en affichant chaque commande avant de la lancer, avec les variables déjà remplacées.
  • À l'intérieur d'un script, set -x active ce mode pour la suite, et set +x le désactive — pratique pour n'observer qu'une portion suspecte.

shellcheck

shellcheck est un analyseur qui relit votre script et signale les erreurs fréquentes (variable non quotée, faute de syntaxe, piège classique), un peu comme un correcteur orthographique pour le Bash. Sous Debian :

sudo apt install shellcheck
shellcheck sauvegarde.sh
astuce

Prenez l'habitude de lancer shellcheck sur chacun de vos scripts avant de les considérer terminés — c'est un réflexe autant apprécié en entreprise qu'un linter pour un langage de développement classique.

Sécurité et lisibilité

  • Ne jamais écrire de mot de passe en clair dans un script (rappel de la séance 3).
  • Restreindre les permissions d'un script d'administration sensible avec chmod 700 script.sh (lecture/écriture/exécution pour le seul propriétaire).
  • Réfléchir avant d'utiliser sudo à l'intérieur d'un script : préférez appeler sudo ./script.sh plutôt que multiplier les sudo à l'intérieur, plus difficile à auditer.
  • Indenter, nommer ses variables clairement, une action par ligne, et commenter le pourquoi d'un choix plutôt que reformuler ce que la ligne fait déjà.

Exemple pratique

Version « brouillon », sans aucune des bonnes pratiques vues aujourd'hui :

#!/bin/bash
rm -rf $dossier/*.tmp
echo "Nettoyage terminé"

Version corrigée :

#!/bin/bash
# Script : nettoyage.sh
# Rôle : supprimer les fichiers temporaires d'un dossier
set -eu

if [ $# -eq 0 ]; then
echo "Usage : $0 <dossier>" >&2
exit 1
fi

dossier="$1"

if [ ! -d "$dossier" ]; then
echo "Erreur : $dossier n'est pas un dossier valide" >&2
exit 1
fi

rm -f "$dossier"/*.tmp
echo "Nettoyage terminé pour $dossier"
exit 0

Sur la version brouillon, shellcheck nettoyage.sh afficherait des avertissements tels que « dossier is referenced but not assigned » et « Double quote to prevent globbing and word splitting » — exactement les deux problèmes que nous venons de corriger.

Test de mémorisation/compréhension


Que fait set -e en début de script ?


Que fait set -u ?


Pourquoi écrire rm -rf "$dossier"/* plutôt que rm -rf $dossier/* ?


Quelle commande lance un script en mode débogage, en affichant chaque commande exécutée ?


À quoi sert >&2 dans un script ?


TP pour réfléchir et résoudre des problèmes

Un script nettoyage.sh volontairement bogué vous est fourni ci-dessous. Vous allez l'auditer et le corriger, point par point.

#!/bin/bash
rm -rf $dossier/*.tmp
echo "Nettoyage terminé"

Étape 1 — Ajouter l'en-tête et set -eu


Bonne pratique - Les 3 premières lignes

Shebang, en-tête, set -eu : ces trois premières lignes devraient devenir un réflexe pour tout script d'administration que vous écrivez à partir de maintenant.

Étape 2 — Corriger le quoting de la ligne dangereuse


Bonne pratique - Le quoting n'est pas de la cosmétique

Une variable non quotée dans une commande de suppression n'est pas qu'une question de style : c'est un risque réel de supprimer les mauvais fichiers, voire tout le système, si la variable est vide ou mal renseignée.

Étape 3 — Vérifier l'argument avant d'agir


Bonne pratique - Séparer résultat et diagnostic

Un message d'erreur destiné à l'utilisateur ou à un fichier de log doit partir sur la sortie d'erreur (>&2), pas sur la sortie standard. Cela permet à quiconque appelle votre script de rediriger proprement chaque flux (./script.sh > resultat.txt 2> erreurs.txt).

Étape 4 — Faire passer le script sous shellcheck


Bonne pratique - Un réflexe à garder

Faites de shellcheck une étape systématique avant de considérer un script terminé, exactement comme vous le feriez avec un linter dans un langage de développement.

📌 Une solution

Ce qu'il faut retenir

NotionRésumé
Shebang#!/bin/bash en première ligne, + chmod +x pour exécuter avec ./script.sh
Variablesnom="valeur" sans espaces, utilisation avec "$nom"
Entrées$1, $#, "$@" pour les arguments ; read -p pour la saisie
Conditionsif [ test ]; then … fi, code de retour $?, 0 = succès
Bouclesfor … in … do … done, while … do … done
Fonctionsnom() { … }, local, return = code de retour
Fiabilitéset -eu, quoting systématique, shellcheck, exit explicite

Aperçu de la prochaine séance

Vous savez désormais écrire un script Bash complet, fiable et lisible : shebang, variables, arguments, conditions, boucles, fonctions et bonnes pratiques. La séance suivante, Bash scripting – niveau 2, ira plus loin : le case pour des choix multiples plus lisibles qu'une cascade de if, les tableaux pour manipuler des listes de valeurs, getopts pour des options en ligne de commande professionnelles (-h, --verbose...), le traitement de texte avec grep, cut, sed et awk, la gestion d'erreur avancée, la planification de tâches avec cron, et pour finir, des interfaces console conviviales avec dialog et whiptail (cours 50_InterfacesConviviales).