Guide des technologies

Bonnes pratiques de sécurité et de robustesse pour les scripts Bash

9 min read Scripting Mis à jour 22 Oct 2025
Bonnes pratiques scripts Bash
Bonnes pratiques scripts Bash

  • Suivez des conventions simples pour éviter les erreurs courantes : shebang portable, citation des variables, set -e et set -o pipefail, variables locales dans les fonctions.
  • Déboguez avec set -o xtrace et préférez la substitution moderne $(...).
  • Utilisez des options explicites (--) et des noms longs pour la lisibilité et la sécurité.

Introduction

Un clavier sur un PC Linux montrant certains caractères spéciaux.

Les scripts Bash sont puissants, mais cette puissance exige de la prudence. Un script mal planifié ou négligent peut causer des pertes de données, des interruptions de service ou ouvrir des failles de sécurité. Cet article rassemble des pratiques éprouvées pour rendre vos scripts plus sûrs, plus lisibles et plus portables.

Public visé : administrateurs systèmes, ingénieurs DevOps, développeurs qui automatisent des tâches avec Bash.

Pourquoi ces pratiques fonctionnent

  • Elles réduisent les erreurs liées à l’interprétation du shell (espaces, expansions inattendues).
  • Elles facilitent la lecture et la maintenance.
  • Elles limitent les effets de bord (variables globales, fichiers mal nommés).

Important
Ces recommandations favorisent la prévention des erreurs courantes. Elles ne remplacent pas une revue de sécurité complète pour des scripts traitant des données sensibles ou déployés en production critique.

Table des matières

  • Utiliser un shebang adapté
  • Toujours citer les variables
  • Arrêter le script en cas d’erreur
  • Gérer explicitement les erreurs
  • Déboguer chaque commande
  • Préférer les options longues et explicites
  • Substitution de commande moderne
  • Valeurs par défaut et fallback
  • Double tiret pour séparer options et arguments
  • Variables locales dans les fonctions
  • Checklist et modèle de revue
  • Exemples, contre-exemples et pièges courants
  • Playbook pour l’écriture d’un script
  • Tests, critères d’acceptation et runbook d’incident
  • Compatibilité et migration
  • Mini-glossaire
  • Résumé

Utiliser un shebang adapté

Le shebang (première ligne de script) indique quel interpréteur doit exécuter le script. Deux approches courantes :

Code d’exemple :

#!/bin/bash
echo "Hello, world"

ou

#!/usr/bin/env bash
echo "Hello, world"
  • /bin/bash garantit l’exécutable à cet emplacement précis. Utile si vous dépendez d’une implémentation spécifique.
  • /usr/bin/env bash utilise le PATH pour trouver bash. C’est plus portable sur des systèmes où bash n’est pas nécessairement dans /bin.

Choix pratique : pour des scripts portables destinés à plusieurs distributions, préférez #!/usr/bin/env bash. Pour des environnements verrouillés et contrôlés, #!/bin/bash peut être plus sûr.

Note importante
Sur certains systèmes embarqués ou minimalistes, env peut ne pas être disponible. Vérifiez la cible.

Toujours citer vos variables

Les espaces et caractères spéciaux dans les noms de fichier sont une source majeure de bugs. Quand Bash développe une variable, l’expansion est littérale :

#!/bin/bash

FILENAME="docs/Letter to bank.doc"
ls $FILENAME

Ceci devient ls docs/Letter to bank.doc et sera interprété comme plusieurs arguments. Solution :

ls "$FILENAME"

Bonne pratique : toujours entourer les expansions de variables par des guillemets doubles à moins d’avoir une raison documentée de ne pas le faire.

Astuce : utilisez aussi les accolades pour délimiter le nom de la variable lorsque vous concaténez du texte :

echo "_${FILENAME}_ est un de mes fichiers"

Arrêter le script en cas d’erreur

Pour éviter qu’un échec passe inaperçu et provoque des effets en cascade, activez :

set -e

Explication courte : dès qu’une commande renvoie un code de sortie non nul, le script s’arrête, sauf si l’erreur est gérée explicitement.

Combiner avec :

set -o pipefail

qui assure qu’une pipeline renvoie une erreur si l’un de ses éléments échoue (et non seulement le dernier).

Attention
set -e modifie le flux par défaut et peut avoir des comportements subtils dans des constructions conditionnelles ou des subshells. Testez vos scripts après activation.

Gérer explicitement les erreurs

En plus de set -e, gérez les erreurs importantes vous-même :

cd "$DIR" || { echo "Échec: impossible d’entrer dans $DIR" >&2; exit 1; }

ou tester le statut de sortie :

commande
if [ $? -ne 0 ]; then
    echo "La commande a échoué" >&2
    exit 1
fi

Préférez la syntaxe avec opérateurs logiques (||) pour plus de concision.

Déboguer chaque commande

Activez le traçage pour voir l’exécution réelle :

set -o xtrace

Cela imprime chaque commande et ses arguments avant exécution, très utile pour diagnostiquer les expansions et substitutions.

Un script utilisant xtrace pour afficher les détails des commandes exécutées — date et ls.

Conseil : activez temporairement set -o xtrace uniquement autour des sections problématiques, ou exportez SHELLOPTS pour l’activer lors du debug.

Préférer les options longues et explicites

Les options courtes (ex. -rf) sont compactes, mais moins lisibles. Dans un script partagé, préférez la version longue :

rm --recursive --force filename

Avantage : lisibilité, auto-documentation du script. Inconvénient : certaines commandes anciennes n’ont pas d’options longues.

Substitution de commande moderne

Préférez $(...) plutôt que les backticks `...` :

VAR=$(ls)

Avantages : facilite le nestage et évite des échappements difficiles.

Déclarer des valeurs par défaut

Utilisez les expansions pour fournir des valeurs par défaut sans code conditionnel :

CMD=${PAGER:-more}

Vous pouvez imbriquer pour des fallback multiples :

DIR=${1:-${HOME:-/home/utilisateur}}

Cette forme est courte, expressive et évite de crowdsourcer la logique de fallback dans du code conditionnel.

Séparer options et arguments avec un double tiret

Les fichiers peuvent commencer par un tiret (-) et être interprétés comme options. Exemple dangereux :

echo "rien" > -a-silly-filename
ls -la
rm *

Si un fichier -rf existe, rm * pourrait se transformer en option. La protection :

rm -- *.md

Le -- indique « tout ce qui suit est un argument, pas une option ». Utilisez-le lorsque l’argument peut provenir d’utilisateurs ou de listes dynamiques.

Variables locales dans les fonctions

Par défaut, les variables dans Bash sont globales. Pour éviter les effets de bord, déclarez-les locales :

function run {
    local DIR=$(pwd)
    echo "faire quelque chose..."
}

Cela limite la portée et évite d’écraser une variable du même nom ailleurs.

Checklist rapide avant commit

  • shebang présent et adapté
  • toutes les expansions de variables sont entre guillemets
  • [ ] set -e et set -o pipefail activés si pertinent
  • [ ] set -o xtrace disponible pour debug (désactivé en prod)
  • [ ] utilisation de -- pour séparer options/arguments quand nécessaire
  • [ ] fonctions avec variables local
  • tests unitaires simples et cas limites couvert
  • commentaires expliquant décisions non évidentes

Playbook pour écrire un script (mini-SOP)

  1. Définir l’objectif et les entrées/sorties attendues.
  2. Choisir shebang en fonction de la cible (portable vs contrôlé).
  3. Valider l’environnement requis (version de Bash minimale, commandes dépendantes).
  4. Écrire des fonctions petites et testables, avec variables locales.
  5. Documenter les options CLI (usage, -h/–help).
  6. Ajouter set -euo pipefail si adapté (voir note ci-dessous).
  7. Écrire des tests et des cas d’échec simulés.
  8. Revue de code et tests d’intégration sur un environnement similaire à la production.

Note sur set -u
set -u traite l’utilisation d’une variable non définie comme une erreur. C’est très utile, mais peut casser des scripts qui s’appuient sur des variables optionnelles. Documentez vos choix.

Exemple complet et annoté

Un petit script démonstratif qui suit les bonnes pratiques :

#!/usr/bin/env bash
set -euo pipefail

usage() {
  echo "Usage: $0 -d directory -p pattern" >&2
  exit 2
}

while getopts ":d:p:h" opt; do
  case $opt in
    d) DIR=$OPTARG ;;
    p) PATTERN=$OPTARG ;;
    h) usage ;;
    \?) echo "Option invalide: -$OPTARG" >&2 ; exit 2 ;;
  esac
done

: "${DIR:?Le paramètre -d est requis}"
: "${PATTERN:=*.md}"

if [ ! -d "$DIR" ]; then
  echo "Répertoire introuvable: $DIR" >&2
  exit 1
fi

# Boucle sûre sur les fichiers
shopt -s nullglob
for f in "$DIR"/$PATTERN; do
  [ -e "$f" ] || continue
  echo "Traitement de: $f"
  # commande fictive sûre
  # traitement ici
done

Points importants du script :

  • set -euo pipefail pour être strict (attention à -u),
  • contrôle des arguments,
  • expansion protégée par guillemets,
  • nullglob pour éviter les motifs non résolus.

Tests et critères d’acceptation

Test de base : le script doit retourner 0 pour un répertoire valide et traiter les fichiers attendus.

Cas de test suggérés :

  • répertoire inexistant → sortie non nulle et message clair (code 1).
  • motif sans fichier → ne pas échouer (retour 0 si comportement attendu).
  • noms de fichiers avec espaces et caractères spéciaux → traitement correct.
  • présence d’un fichier commençant par - → aucune option mal interprétée.

Runbook d’incident minimal

Si un script en production échoue :

  1. Lire les logs et le trace (xtrace si activé).
  2. Restaurer l’état connu (rollback) si une opération destructive a commencé.
  3. Isoler le système si nécessaire pour éviter propagation.
  4. Reproduire l’erreur sur une copie locale avec set -x pour diagnostiquer.
  5. Patch: corriger l’expansion problématique ou ajouter des vérifications d’entrée.
  6. Déployer une version testée et surveiller.

Sécurité
Ne stockez jamais de secrets en clair dans un script. Préférez des variables d’environnement injectées au runtime ou des gestionnaires de secrets.

Contre-exemples et quand ces règles échouent

  • set -e peut rendre le contrôle du flux plus difficile dans des constructions complexes (ex. if qui appelle des commandes avec retours non-nuls attendus).
  • set -u casse des scripts qui utilisent des variables optionnelles non définies.
  • L’usage de guillemets bloque la séparation voulue dans des cas où vous attendez une expansion en plusieurs éléments — documentez ces exceptions.

Alternative
Si votre script devient trop complexe, envisagez de le réécrire en Python ou en Go, qui offrent des bibliothèques robustes pour le parsing d’arguments et la gestion d’erreurs.

Modèle mental et heuristiques

  • Principe du moindre privilège : n’exécutez pas de commandes avec plus de droits que nécessaire.
  • Définissez des frontières claires : fonctions petites, tests unitaires simples.
  • Assumez le pire : entrées malformées, permission manquante, disque plein.

Maturité d’un projet de scripts

  • Niveau 1 (ad hoc) : scripts courts, usage personnel, peu de tests.
  • Niveau 2 (standardisé) : shebang, options set, checklist, revue de code.
  • Niveau 3 (production) : tests automatisés, CI, gestion des versions, monitoring et runbooks.

Compatibilité et migration

  • Vérifiez la version minimale de Bash requise. Certaines expansions (ex. ${var:-}) sont POSIX, d’autres sont spécifiques à Bash.
  • Pour portabilité POSIX, visez /bin/sh compatible et évitez les extensions Bash ( [[ ]], =~, proc substitution).
  • Migrer vers un langage plus structuré (Python) si : long code, besoins complexes de parsing, gestion d’objets.

Modèle de décision (Mermaid)

flowchart TD
  A[Début] --> B{Script simple ou complexe ?}
  B -- Simple --> C[Conserver en Bash]
  B -- Complexe --> D[Évaluer Python/Go]
  C --> E{Dépendances système ?}
  E -- Non --> F[Shebang /usr/bin/env bash]
  E -- Oui --> G[Shebang /bin/bash]
  D --> H[Choisir langage, écrire tests]

Rôle-based checklist

  • Développeur : tests unitaires, commentaires, usage --help.
  • Opérations : monitoring, runbook d’incident, variables d’environnement injectées proprement.
  • Sécurité : gestion des secrets hors du script, revue pour privilèges escalade.

Templates utiles

Template d’entête pour chaque script :

#!/usr/bin/env bash
# NOM: bref descriptif
# USAGE: script.sh -a arg -b arg
# DEPENDANCES: ls, awk, sed
set -euo pipefail

# variables globales

Template d’aide CLI :

usage() {
  cat <

Montage de sécurité et bonnes habitudes

  • Ne pipez jamais sans vérifier : cmd | less peut masquer des erreurs en aval.
  • Ne redirigez pas vers des fichiers sensibles sans vérification (> écrase).
  • Évitez d’exécuter eval sur du contenu non contrôlé.

Mini-glossaire

  • shebang : ligne #! en tête d’un script qui déclare l’interpréteur.
  • expansion : remplacement d’une variable par sa valeur.
  • pipeline : séquence cmd1 | cmd2 où la sortie standard de cmd1 est entrée de cmd2.

Résumé

  • Protégez vos scripts en citant systématiquement vos variables, en utilisant set -e et set -o pipefail, et en déclarant des variables locales dans les fonctions.
  • Préférez la lisibilité (options longues, $(...), --) et testez les cas limites (noms avec espaces, fichiers commençant par -).
  • Pour des scripts complexes ou critiques, ajoutez tests, CI, runbooks et envisagez un langage plus structuré.

FAQ

Comment choisir entre /bin/bash et /usr/bin/env bash ?

Choisissez env pour la portabilité (si bash est dans le PATH). Choisissez l’emplacement absolu si vous dépendez d’une installation spécifique et contrôlée.

Dois-je toujours utiliser set -u ?

set -u aide à détecter des variables non initialisées, mais il peut casser des scripts qui comptent sur des variables optionnelles. Évaluez l’impact et documentez.

Que faire si un fichier commence par - ?

Utilisez -- pour séparer options et arguments, par exemple rm -- -fichierspécial.


Un terminal montrant la création d'un nom de fichier avec un tiret initial puis la confirmation de son existence.

Message d'erreur de ls indiquant

Auteur
Édition

Matériaux similaires

Installer et utiliser Podman sur Debian 11
Conteneurs

Installer et utiliser Podman sur Debian 11

Guide pratique : apt-pinning sur Debian
Administration système

Guide pratique : apt-pinning sur Debian

OptiScaler : activer FSR 4 dans n'importe quel jeu
Jeux PC

OptiScaler : activer FSR 4 dans n'importe quel jeu

Dansguardian + Squid NTLM sur Debian Etch
réseau

Dansguardian + Squid NTLM sur Debian Etch

Corriger l'erreur d'installation Android sur SD
Android, Dépannage

Corriger l'erreur d'installation Android sur SD

KNetAttach et remote:/ — Dossiers réseau KDE
Tutoriel

KNetAttach et remote:/ — Dossiers réseau KDE