Ce guide rassemble tout pour écrire un script shell Bash, du shebang aux fonctions. Variables, conditions, boucles, arguments, codes de sortie, débogage : chaque notion est expliquée avec un exemple exécutable, puis approfondie dans une page dédiée. C'est le guide de référence de la section scripting, à lire d'un bout à l'autre pour apprendre, ou à consulter section par section.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Créer et exécuter un script avec shebang et
chmod +x - Utiliser des variables pour stocker et réutiliser des valeurs
- Écrire des conditions
if/then/fiavec[[ ]] - Répéter des actions avec
foretwhile - Organiser le code avec des fonctions
- Gérer les arguments
$1,$#,$@et les codes de retour$? - Déboguer avec
bash -xetshellcheck
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »Un script Bash est utile dès que vous répétez la même séquence de commandes plus d'une ou deux fois :
- vérifier l'espace disque sur dix serveurs et envoyer une alerte si un seuil est dépassé
- créer un utilisateur avec son répertoire home, ses clés SSH et ses permissions en une commande
- générer un rapport quotidien à partir des logs nginx et l'envoyer par e-mail
- valider l'état d'un service avant de lancer un déploiement
- convertir ou renommer un lot de fichiers selon un pattern
En préparation RHCSA, les scripts shell sont au programme de façon explicite : vous devez être capable d'en écrire un à partir de zéro sous contrainte de temps.
Créer et exécuter son premier script
Section intitulée « Créer et exécuter son premier script »La première ligne d'un script, le shebang, indique quel interpréteur l'exécutera. Sans elle, c'est le shell qui lance le script qui décide : Bash l'exécute avec Bash, mais dash (le sh de Debian et Ubuntu) l'exécute avec lui-même, et tout ce qui est propre à Bash casse. Mesuré sur Debian 13 : le même fichier sans shebang voit BASH_VERSION défini depuis Bash, absent depuis dash.
#!/bin/bash
echo "Bonjour, $USER !"Enregistrez ce fichier sous bonjour.sh, puis rendez-le exécutable et lancez-le depuis le répertoire courant, où le shell ne cherche pas les commandes par défaut :
chmod +x bonjour.sh./bonjour.shBonjour, bob !#!/bin/bash: le shebang, le système lit ce chemin et lance cet interpréteurchmod +x: ajoute le droit d'exécution au propriétaire./: exécute le script depuis le répertoire courant
Variables
Section intitulée « Variables »Une variable stocke une valeur pour la réutiliser plus loin. En Bash, la règle qui piège tous les débutants tient dans l'espacement : la déclaration se fait sans espace autour du = (nom="alice", jamais nom = "alice"), car avec des espaces Bash croit exécuter une commande nommée nom. À la lecture, on préfixe le nom par $ ou on l'entoure de ${}, cette dernière forme étant obligatoire dès qu'on colle du texte juste après le nom.
#!/bin/bash
nom="alice"nombre=42
echo "Utilisateur : $nom"echo "Valeur : ${nombre}"Utilisateur : aliceValeur : 42Substitution de commande
Section intitulée « Substitution de commande »La substitution de commande capture la sortie standard d'une commande et la place dans une variable. La forme moderne $(...) remplace l'ancienne notation avec des accents graves : elle s'imbrique proprement et se lit sans ambiguïté. C'est le mécanisme qui permet à un script de réagir à l'état réel du système, par exemple la date du jour ou le nombre de comptes présents.
date_du_jour=$(date '+%Y-%m-%d')nb_utilisateurs=$(wc -l < /etc/passwd)
echo "Date : $date_du_jour"echo "Comptes : $nb_utilisateurs"Date : 2026-04-10Comptes : 42Lire une entrée utilisateur
Section intitulée « Lire une entrée utilisateur »La commande read met le script en pause et attend une saisie au clavier, qu'elle range dans la variable indiquée. C'est utile pour un script interactif, mais gardez en tête qu'un script automatisé (cron, CI) n'a personne pour répondre : réservez read aux outils lancés à la main.
echo "Entrez votre nom :"read -r prenomecho "Bonjour, $prenom !"L'option -r désactive l'interprétation des antislashes de la saisie : un chemin Windows collé ou une ligne contenant \n ressort intact. Utilisez toujours read -r, shellcheck signale l'autre forme.
Variables automatiques utiles
Section intitulée « Variables automatiques utiles »Bash pose lui-même quelques variables, sans déclaration. Ces quatre-là reviennent dans presque tout script d'administration, pour nommer un fichier, construire un chemin ou tirer une valeur aléatoire :
| Variable | Contenu |
|---|---|
$USER | Nom de l'utilisateur courant |
$HOME | Répertoire personnel |
$PWD | Répertoire courant |
$RANDOM | Nombre aléatoire entre 0 et 32767 |
Conditions
Section intitulée « Conditions »La structure if/then/fi exécute un bloc si une condition est vraie. Utilisez [[ ]] (doubles crochets), plus sûr et plus lisible que [ ].
#!/bin/bash
fichier="$1"
if [[ -f "$fichier" ]]; then echo "Le fichier existe."else echo "Le fichier est introuvable."fiTests sur les fichiers
Section intitulée « Tests sur les fichiers »En administration système, la moitié des conditions portent sur des fichiers : le script existe-t-il, le répertoire est-il présent, ai-je le droit d'écrire ici. Ces opérateurs à une lettre renvoient vrai ou faux et s'utilisent tels quels dans [[ ]]. Le plus courant est -f, qui vérifie qu'un fichier ordinaire existe avant de le lire.
| Opérateur | Condition vraie si… |
|---|---|
-f "$f" | $f est un fichier ordinaire |
-d "$f" | $f est un répertoire |
-e "$f" | $f existe (fichier ou répertoire) |
-r "$f" | $f est lisible |
-w "$f" | $f est modifiable |
-x "$f" | $f est exécutable |
Tests sur les chaînes
Section intitulée « Tests sur les chaînes »Les tests de chaînes servent surtout à valider un argument : est-il vide, vaut-il exactement telle valeur. Le piège classique est -z contre -n : -z est vrai quand la chaîne est vide, -n quand elle contient quelque chose. Dans [[ ]], une variable vide non quotée reste un opérande vide et le test fonctionne ; dans l'ancienne commande [ ], elle disparaît et provoque unary operator expected. Gardez les guillemets par habitude : ils protègent dans les deux cas.
| Opérateur | Condition vraie si… |
|---|---|
-z "$s" | $s est vide |
-n "$s" | $s n'est pas vide |
"$a" == "$b" | les deux chaînes sont égales |
"$a" != "$b" | les deux chaînes sont différentes |
Tests numériques
Section intitulée « Tests numériques »Bash distingue nettement comparaison de nombres et comparaison de chaînes. Pour les nombres, on utilise ces opérateurs à deux lettres (-eq, -lt...), pas les signes < ou > qui, eux, comparent alphabétiquement. Écrire [[ "$a" -gt "$b" ]] compare bien 9 et 10 comme des entiers, là où > placerait « 10 » avant « 9 ».
| Opérateur | Signification |
|---|---|
-eq | égal |
-ne | différent |
-lt | inférieur strict |
-le | inférieur ou égal |
-gt | supérieur strict |
-ge | supérieur ou égal |
score=85
if [[ $score -ge 90 ]]; then echo "Excellent"elif [[ $score -ge 70 ]]; then echo "Bien"else echo "À améliorer"fiBienfor, itérer sur une liste
Section intitulée « for, itérer sur une liste »La boucle for répète un bloc pour chaque élément d'une liste. Cette liste peut être écrite à la main, produite par un motif de fichiers (/etc/*.conf) ou une plage {1..5}. C'est la forme à privilégier quand vous connaissez d'avance l'ensemble des éléments à traiter.
for service in nginx ssh cron; do echo "Service : $service"doneService : nginxService : sshService : cronSur les fichiers d'un dossier :
for fichier in /etc/*.conf; do echo "Config : $fichier"doneSur une plage numérique :
for i in {1..5}; do echo "Itération $i"donewhile, boucle conditionnelle
Section intitulée « while, boucle conditionnelle »Là où for parcourt un ensemble connu, while répète tant qu'une condition reste vraie, sans savoir d'avance combien de tours auront lieu. C'est la boucle à choisir pour attendre qu'un service démarre, ou pour consommer une entrée jusqu'à épuisement. Veillez à ce que la condition finisse par devenir fausse, sinon la boucle tourne indéfiniment.
compteur=1
while [[ $compteur -le 3 ]]; do compteur=$((compteur + 1)) echo "Tour $compteur"doneTour 2Tour 3Tour 4Lire un fichier ligne par ligne
Section intitulée « Lire un fichier ligne par ligne »Lire un fichier ligne à ligne est un besoin quotidien : parcourir une liste de serveurs, traiter un journal, appliquer une action par entrée. La combinaison while IFS= read -r ligne est le patron correct pour le faire sans surprise. Sans le IFS=, Bash supprime les espaces de début et de fin ; sans le -r, il interprète les antislashes. Les deux ensemble garantissent que chaque ligne arrive intacte.
while IFS= read -r ligne; do echo "→ $ligne"done < /etc/hostnameIFS=: conserve les espaces et tabulations de début et de fin de ligne, quereadretire sinon-r: préserve les backslashes
Fonctions
Section intitulée « Fonctions »Une fonction regroupe des instructions réutilisables sous un nom, pour éviter de répéter le même bloc à plusieurs endroits. En Bash, une contrainte vaut d'être connue tout de suite : une fonction doit être déclarée avant la ligne qui l'appelle, car le script est lu de haut en bas. Placez donc vos fonctions en tête de fichier, juste après le shebang.
#!/bin/bash
afficher_entete() { local titre="$1" echo "" echo "=== $titre ==="}
afficher_entete "Rapport système"afficher_entete "Fin"=== Rapport système ===
=== Fin ===local: la variable reste locale à la fonction, indispensable pour éviter les effets de bord$1,$2… : arguments passés à la fonction (indépendants des arguments du script)
Retourner une valeur textuelle
Section intitulée « Retourner une valeur textuelle »Une fonction retourne un code de sortie (0–255), pas une chaîne. Pour retourner du texte, affichez-le avec echo et capturez-le avec $() :
nom_complet() { local prenom="$1" local nom="$2" echo "${prenom} ${nom}"}
resultat=$(nom_complet "alice" "martin")echo "Bonjour, $resultat !"Bonjour, alice martin !Arguments du script
Section intitulée « Arguments du script »Quand vous passez des arguments à votre script, ils sont accessibles via des variables spéciales, posées par Bash au lancement :
| Variable | Contenu |
|---|---|
$0 | Nom du script |
$1, $2… | Premier, deuxième argument |
$# | Nombre total d'arguments |
$@ | Tous les arguments (liste) |
#!/bin/bash
if [[ $# -ne 2 ]]; then echo "Usage: $0 <source> <destination>" >&2 exit 1fi
echo "Script : $0"echo "Source : $1"echo "Dest : $2"echo "Nb args : $#"./copier.sh /etc/hosts /tmp/hosts.bakScript : ./copier.shSource : /etc/hostsDest : /tmp/hosts.bakNb args : 2Codes de sortie
Section intitulée « Codes de sortie »Chaque commande se termine avec un code de sortie : 0 = succès, valeur non nulle = erreur. $? contient le code de la dernière commande exécutée.
ls /etc/passwdecho "Code : $?" # 0 : succès
ls /fichier-inexistant 2>/dev/nullecho "Code : $?" # 2 : erreurDans un script, exit définit le code retourné à l'appelant, qu'il s'agisse d'un shell, de cron ou d'un pipeline CI :
if [[ ! -d "$1" ]]; then echo "Erreur : '$1' n'est pas un répertoire." >&2 exit 1fi
# … traitement …exit 0Exemple complet
Section intitulée « Exemple complet »Voici un script réaliste qui rassemble tout ce qui précède : une fonction d'affichage, la validation du nombre d'arguments, un test de répertoire, une boucle for de comptage et des codes de sortie explicites. Lisez-le comme un modèle à réutiliser : la ligne set -euo pipefail en tête et la validation d'argument au début sont exactement les réflexes attendus sur un script d'administration sérieux.
#!/bin/bash# generer-rapport.sh : rapport d'un répertoire# Usage: ./generer-rapport.sh <repertoire>
set -euo pipefail
afficher_entete() { local titre="$1" echo "" echo "=== $titre ==="}
if [[ $# -ne 1 ]]; then echo "Usage: $0 <repertoire>" >&2 exit 1fi
repertoire="$1"
if [[ ! -d "$repertoire" ]]; then echo "Erreur : '$repertoire' n'est pas un répertoire." >&2 exit 1fi
afficher_entete "Rapport : $repertoire"echo "Date : $(date '+%Y-%m-%d %H:%M')"echo "Utilisateur: $USER"echo ""
nb_fichiers=0for entree in "$repertoire"/*; do [[ -f "$entree" ]] && nb_fichiers=$((nb_fichiers + 1))done
echo "Fichiers : $nb_fichiers"exit 0./generer-rapport.sh /etc=== Rapport : /etc ===Date : 2026-04-10 07:44Utilisateur: bob
Fichiers : 158Déboguer un script
Section intitulée « Déboguer un script »bash -x, voir chaque commande avant exécution
Section intitulée « bash -x, voir chaque commande avant exécution »Lancé par bash -x, le script affiche chaque ligne exécutée avant de la lancer, préfixée de +, avec les variables déjà substituées. C'est le moyen le plus rapide de suivre l'exécution pas à pas et de repérer la valeur qui dérape.
bash -x ./generer-rapport.sh /etcset -x / set +x dans le script
Section intitulée « set -x / set +x dans le script »Lancer tout le script avec bash -x devient vite illisible sur un script long. Les directives set -x et set +x encadrent la seule portion qui vous intéresse : le traçage démarre à la première, s'arrête à la seconde. C'est la méthode à privilégier pour isoler une commande suspecte sans noyer la sortie.
set -x # début du debugmv "$src" "$dst"set +x # fin du debugshellcheck, analyse statique
Section intitulée « shellcheck, analyse statique »shellcheck est un analyseur statique : il lit le script sans l'exécuter et signale erreurs de syntaxe et pièges classiques, à commencer par les variables non quotées. C'est le premier réflexe à adopter avant de lancer un script, et l'outil que les correcteurs d'examen comme les pipelines CI utilisent pour juger la qualité d'un script. Chaque avertissement porte un code (SC2086, etc.) qui renvoie à une explication détaillée en ligne.
Sur Debian et Ubuntu, shellcheck est dans les dépôts de la distribution. Sur RHEL et ses dérivées, il n'y est pas : il vient d'EPEL, sous le nom de paquet ShellCheck. Mesuré sur AlmaLinux 10 : dnf install shellcheck échoue sans EPEL, et ShellCheck s'installe une fois epel-release en place.
# Debian/Ubuntusudo apt install shellcheck
# RHEL et dérivées : le paquet vient d'EPELsudo dnf install epel-releasesudo dnf install ShellCheckshellcheck mon-script.shSur un script qui écrit rm $f, shellcheck 0.10 répond ainsi. L'option -f gcc donne le même constat sur une seule ligne, pratique dans un éditeur ou une CI :
In mon-script.sh line 3:rm $f ^-- SC2086 (info): Double quote to prevent globbing and word splitting.
Did you mean:rm "$f"Dépannage
Section intitulée « Dépannage »Les erreurs de scripting débutant se répètent d'un script à l'autre, et la plupart se lisent dès le message d'erreur. Les messages ci-dessous sont ceux de Bash 5.2, relevés sur Debian 13 et AlmaLinux 10. Deux réflexes couvrent presque tous les cas : vérifier le droit d'exécution et le shebang pour les problèmes de lancement, lancer shellcheck pour les erreurs de syntaxe.
| Symptôme | Cause probable | Correction |
|---|---|---|
Permission denied à l'exécution | Droit +x absent | chmod +x mon-script.sh |
cannot execute: required file not found (Bash 5.2) ou bad interpreter (autres shells) | Shebang incorrect ou fins de ligne Windows (CRLF) : le système cherche bash\r | Vérifier #!/bin/bash ; convertir avec dos2unix mon-script.sh |
unbound variable | Variable non définie utilisée avec set -u | Initialiser la variable ou utiliser ${VAR:-} |
[[: not found ou declare: not found | Script lancé avec sh script.sh : dash ignore le shebang | Lancer avec ./script.sh ou bash script.sh |
syntax error near unexpected token | fi/done/guillemet manquant | Lancer shellcheck script.sh pour localiser |
À retenir
Section intitulée « À retenir »- Un script commence toujours par
#!/bin/bashet se rend exécutable avecchmod +x. - Les variables se déclarent sans espace autour du
=et s'utilisent entre guillemets :"$var". - Utilisez
[[ ]]pour les tests, plus sûr que[ ]. - Les fonctions déclarent leurs variables internes avec
local. $1,$2,$#,$@donnent accès aux arguments passés au script.exit 0= succès,exit 1= erreur, toujours explicite.bash -xpour déboguer,shellcheckpour détecter les erreurs à l'avance.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : Variables et paramètres Bash
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications