CyberSDF

Reprenez la main sur votre numérique

Écrire un script shell Bash : du fichier au résultat vérifié

Schéma d’un script Bash reliant un dossier d’essai, une fonction de comptage et un code de retour

Écrire un script Bash consiste à choisir son interpréteur, valider les arguments, regrouper le travail dans une fonction et retourner un code explicite. L’exemple ci-dessous compte les fichiers d’un dossier sans casser les noms contenant des espaces, puis montre la sortie normale, l’argument manquant et le dossier introuvable.

Sommaire
  1. Commencez par un script qui fait une seule chose
  2. Choisissez Bash avant d’écrire la première commande
  3. Faites de l’argument une entrée contrôlée
  4. Protégez les chemins avant de compter
  5. Faites parler le script avec stdout, stderr et les codes
  6. Lancez le fichier dans un dossier d’essai
  7. Ne laissez pas set -e choisir votre logique
  8. Sachez quand Bash n’est plus le bon outil

Le 18 septembre 2026, l’essai local a utilisé Bash 5.2.21 et GNU findutils 4.9.0. Le fichier a passé bash -n, compté trois fichiers de test et renvoyé les codes 0, 64 et 66 sur ses trois chemins utiles.

Commencez par un script qui fait une seule chose

Créez un fichier nommé compter-fichiers.sh, puis copiez ce contenu. Il reçoit un seul dossier, vérifie cette entrée et compte les fichiers réguliers placés immédiatement dedans.

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

usage() {
    printf 'Usage : %s DIRECTORY\n' "${0##*/}" >&2
}

main() {
    if (( $# != 1 )); then
        usage
        return 64
    fi

    local directory=$1
    if [[ ! -d "$directory" ]]; then
        printf 'Erreur : dossier introuvable : %s\n' "$directory" >&2
        return 66
    fi

    local count=0
    local file
    while IFS= read -r -d '' file; do
        count=$((count + 1))
    done < <(find "$directory" -maxdepth 1 -type f -print0)

    printf '%d fichier(s) dans %s\n' "$count" "$directory"
}

main "$@"

La dernière ligne transmet tous les arguments à main. La fonction valide ensuite leur nombre avant de lire $1. Cette petite séparation permet de relire l’entrée, le travail et la sortie sans chercher dans un bloc de commandes mélangées.

Enregistrez le fichier, puis vérifiez sa syntaxe avant de l’exécuter :

bash -n compter-fichiers.sh
chmod +x compter-fichiers.sh

bash -n lit la syntaxe sans lancer les commandes. Le manuel Bash sur les scripts décrit ce fichier comme une suite de commandes lue par Bash dans une session non interactive. Le contrôle de syntaxe ne remplace pas l’essai sur une cible sans valeur.

Choisissez Bash avant d’écrire la première commande

La première ligne, appelée shebang, indique l’interpréteur attendu quand le fichier est lancé directement. #!/usr/bin/env bash demande au système de chercher bash dans le PATH. Elle correspond au langage utilisé ici, notamment pour [[ ]], local et la substitution de processus.

Deux appels montrent la différence entre l’interpréteur choisi et le droit d’exécution :

bash compter-fichiers.sh /tmp/atelier-shell
./compter-fichiers.sh /tmp/atelier-shell

Le premier appel donne le fichier à Bash comme argument. Le second laisse le système lire le shebang, à condition que le fichier possède le bit d’exécution ajouté par chmod +x. Dans les deux cas, /tmp/atelier-shell devient l’argument du script.

N’écrivez pas #!/bin/sh pour obtenir un script plus portable après coup. Ce choix engage la syntaxe POSIX et impose de remplacer les constructions spécifiques à Bash. La spécification POSIX du langage shell décrit ce socle commun. Ici, le fichier annonce Bash et assume ce choix.

Pour replacer le geste dans l’ensemble des commandes disponibles, partez de l’orientation Linux et terminal. Elle mène aux explications sur les fichiers, les droits et les diagnostics sans faire de ce script une liste de commandes isolées.

Faites de l’argument une entrée contrôlée

Les paramètres positionnels donnent au script les mots placés après son nom. $1 est le premier, $# compte les arguments et "$@" les retransmet en conservant leurs frontières. Le manuel Bash sur les paramètres détaille ces valeurs spéciales.

La validation de $# arrive avant l’accès à $1. Un appel sans argument affiche donc la ligne d’usage et renvoie 64, au lieu de lancer une commande avec une cible vide. Dans un script plus large, la même règle permet d’ajouter des options sans laisser un appel incomplet se propager.

La fonction usage concentre le texte destiné à l’utilisateur. La fonction main porte l’action. Une fonction Bash regroupe des commandes sous un nom, reçoit ses propres paramètres et peut rendre un statut avec return ; c’est le comportement décrit par le manuel des fonctions Bash.

La déclaration local directory=$1 garde le chemin dans la fonction. Le script ne crée pas une variable globale qui pourrait porter un ancien chemin si d’autres fonctions apparaissent plus tard. Le mot local est une construction Bash, une raison supplémentaire de conserver le shebang cohérent avec le corps.

Un nom d’argument peut contenir des espaces. C’est pourquoi le test écrit "$directory" et la transmission finale "$@". Une expansion non protégée peut être redécoupée par le shell avant l’appel de la commande, même si la valeur semblait n’être qu’un chemin.

Protégez les chemins avant de compter

Le shell interprète les espaces et les caractères de motif avant de lancer une commande. Une variable non entourée de guillemets peut donc produire plusieurs arguments, ou transformer une étoile en recherche de noms. La fiche ShellCheck SC2086 illustre précisément ce risque de découpage et d’expansion.

Le script protège le chemin transmis à find, puis demande à find d’émettre les noms séparés par un octet nul avec -print0. La boucle read -r -d '' file utilise le même séparateur. Le nom rapport final.txt reste alors un seul nom, au lieu de devenir deux mots.

find "$directory" -maxdepth 1 -type f -print0

Dans cette commande, -maxdepth 1 garde le comptage sur le contenu direct du dossier, et -type f retient les fichiers réguliers. Le fichier masqué .fichier-cache est trouvé, tout comme un nom contenant une étoile, car la recherche ne passe pas par une expansion de glob du shell.

Évitez le raccourci for file in $files quand les noms viennent d’une commande. Il mélange la sortie textuelle, les espaces et les motifs de fichiers. Si l’opération porte sur des chemins, faites produire des chemins sûrs par la commande qui les connaît et transmettez-les sans les redécouper.

Les repères de pwd, ls et des chemins relatifs restent utiles avant l’écriture d’un fichier. La page sur les commandes Linux de base les pratique dans un dossier d’essai. Le présent script commence plus loin : il transforme déjà cette lecture en petit programme avec une entrée et un statut.

Faites parler le script avec stdout, stderr et les codes

Le résultat normal part sur la sortie standard avec printf. L’usage et l’erreur de dossier partent sur la sortie d’erreur grâce à >&2. Cette séparation permet à une commande appelante de conserver le résultat sans mélanger un diagnostic avec la donnée comptée.

Situation Message Code
Un dossier valide 3 fichier(s) dans ... 0
Un argument manquant Usage : ... 64
Un dossier absent Erreur : dossier introuvable : ... 66

Le code 0 signifie que le traitement demandé s’est terminé normalement. Les codes 64 et 66 distinguent ici une mauvaise forme d’appel d’une cible inexistante. Ils ne remplacent pas le message, mais ils permettent à un autre script de décider sans analyser une phrase.

if ./compter-fichiers.sh /tmp/atelier-shell; then
    printf 'Comptage termine.\n'
else
    printf 'Le comptage a echoue.\n' >&2
fi

Le statut de la fonction devient celui de l’appel final main "$@". Le shell appelant peut ainsi utiliser la réussite ou l’échec dans un if, une suite && ou une étape de travail. La règle POSIX sur le statut de sortie des commandes formalise ce contrat.

Si une commande répond Permission denied, ne remplacez pas immédiatement le chemin par sudo. Lisez l’utilisateur, le propriétaire et le mode dans l’explication des permissions Linux, puis décidez si le script doit réellement écrire à cet endroit. Compter des fichiers n’exige pas de privilège administrateur.

Lancez le fichier dans un dossier d’essai

Utilisez un dossier temporaire dont vous reconnaissez le nom. Les trois fichiers ci-dessous couvrent un nom avec espace, une étoile littérale et un fichier masqué :

mkdir -p /tmp/cybersdf-script-shell-20260918
printf 'un\n' > "/tmp/cybersdf-script-shell-20260918/rapport final.txt"
printf 'deux\n' > "/tmp/cybersdf-script-shell-20260918/notes*.txt"
printf 'trois\n' > "/tmp/cybersdf-script-shell-20260918/.fichier-cache"

bash -n compter-fichiers.sh
chmod +x compter-fichiers.sh
./compter-fichiers.sh /tmp/cybersdf-script-shell-20260918

La sortie observée le 18 septembre 2026 est :

3 fichier(s) dans /tmp/cybersdf-script-shell-20260918

Le code de retour observé juste après cet appel est 0. Pour lire les deux chemins négatifs, lancez-les séparément :

./compter-fichiers.sh /tmp/cybersdf-script-shell-20260918/inexistant
echo $?
./compter-fichiers.sh
echo $?

Les sorties et statuts observés sont les suivants :

Erreur : dossier introuvable : /tmp/cybersdf-script-shell-20260918/inexistant
66
Usage : compter-fichiers.sh DIRECTORY
64

Le dossier d’essai permet de répéter la commande sans toucher à des documents personnels. Si l’affichage ne correspond pas, relisez le chemin passé au script et vérifiez la syntaxe avec bash -n avant de modifier le code. Le message d’erreur donne déjà la cible que le programme n’a pas trouvée.

Ne laissez pas set -e choisir votre logique

L’en-tête set -euo pipefail active trois comportements Bash utiles dans un petit script :

Ces options ne remplacent pas les validations explicites. Une commande peut réussir tout en produisant un résultat inutilisable, et une commande peut renvoyer un statut non nul dans un test prévu par la logique. Les validations explicites de $# et [[ -d "$directory" ]] restent donc dans le script. La documentation du builtin set décrit les contextes dans lesquels ses options s’appliquent.

La protection la plus fréquente reste le guillemet au bon endroit :

fichier='rapport final.txt'
cat "$fichier"

Ici, "$fichier" conserve le nom comme un seul argument. Avec cat $fichier, Bash peut séparer rapport et final.txt. Cette différence apparaît avant que cat ne reçoive la commande.

Quand le script grandit, testez une branche nominale et une branche négative pour chaque entrée importante. Une commande copiée depuis un forum ne devient pas fiable parce qu’elle est placée dans une fonction. Lisez aussi les avertissements de ShellCheck lorsque l’outil est disponible, puis revenez à la question concrète : quel chemin est touché, quelle sortie est produite et quel code est rendu ?

Sachez quand Bash n’est plus le bon outil

Bash convient pour relier des commandes existantes, contrôler quelques arguments et transmettre des fichiers entre étapes. Le fichier devient moins lisible lorsque l’état, les structures de données, les erreurs et les tests prennent plus de place que les commandes appelées.

Si la cible est un environnement qui ne fournit que sh, écrivez dès le début en syntaxe POSIX avec #!/bin/sh. Si vous avez besoin de tableaux Bash, de [[ ]] ou de fonctions avec local, gardez le shebang Bash et indiquez ce choix dans le nom et la documentation du fichier. Changer uniquement la première ligne ne convertit pas le langage.

Le prochain besoin peut être une routine complète, avec entrée, transformation, sortie et contrôle. La page qui décompose une petite routine d’automatisation aide à décider quelles vérifications écrire avant d’enchaîner plusieurs commandes.

À partir de là, chaque script mérite le même examen : une entrée nommée, une action compréhensible, une sortie lisible et un échec que l’appelant peut traiter. C’est cette structure qui transforme un fichier de commandes en outil réutilisable.