Mini-projet : premier script shell
Créer un premier script shell affichant l’état du système.
Objectifs
À la fin de cette étape, vous saurez :
- transformer une suite de commandes tapées à la main en fichier réexécutable ;
- utiliser le shebang et
chmod +x, et expliquer ce que fait chacun ; - combiner substitution
$(...), code de retour et test[ ... ]dans un script ; - faire de votre script un vrai outil Unix : il choisit lui-même son code de sortie.
🔗 Pour vous rafraîchir la mémoire : observer les codes de retour · les variables d'environnement
Pourquoi des scripts ?
Depuis le début du chapitre, vous tapez des commandes une par une. Un script est simplement ces commandes, écrites d'avance dans un fichier : le shell les lit et les exécute ligne par ligne. Trois bénéfices immédiats :
- Répétable : la séquence s'exécute à l'identique, sans risque d'oubli ;
- Transmissible : on peut lire, corriger, partager le fichier — c'est du code ;
- Automatisable : un script peut en appeler un autre, être déclenché par une tâche planifiée... C'est ainsi que fonctionnent les sauvegardes et les déploiements que vous croiserez plus tard.
Le script que nous construisons ici est volontairement modeste (un « rapport d'état » du système), mais il contient tous les ingrédients d'un vrai outil.
Anatomie minimale
#!/bin/bash
echo "Bonjour, je suis $(whoami)."
echo "Répertoire courant : $(pwd)"
Deux éléments méritent l'arrêt sur détail :
La première ligne, le shebang (#!) : elle indique quel interpréteur doit exécuter la suite du fichier. Quand vous lancez ./mon-script.sh, le noyau lit ces deux premiers caractères, comprend « ce fichier n'est pas un binaire mais un script », et lance /bin/bash avec votre fichier en argument. Sans shebang, le résultat dépend de qui lance quoi — avec lui, le comportement est défini partout.
$(commande), la substitution de commande : le shell exécute la commande entre parenthèses et remplace l'ensemble par sa sortie avant d'exécuter la ligne. $(whoami) devient cours, $(date) devient la date actuelle. Vous l'avez déjà utilisée au chapitre 1 (echo "Ajouté le $(date)") ; dans un script, elle devient quotidienne.
Rendre le fichier exécutable
Un fichier de texte n'est pas exécutable par défaut. Souvenez-vous de ls -l :
$ touch essai.sh
$ ls -l essai.sh
-rw-r--r-- 1 cours cours 0 Jan 12 11:00 essai.sh
Les droits commencent par -rw-r--r-- : lecture (r) et écriture (w), pas de x (execute). Ajoutons-le :
$ chmod +x essai.sh
$ ls -l essai.sh
-rwxr-xr-x 1 cours cours 0 Jan 12 11:02 essai.sh
Les trois premiers rwx sont vos droits de propriétaire : le x est apparu. Le système peut désormais lancer ce fichier directement avec ./essai.sh. (Sans chmod, il reste possible de passer par l'interpréteur explicitement : bash essai.sh — le shebang est alors inutile puisque c'est vous qui choisissez bash.)
Les briques du rapport : variables, tests, sorties
Voici le squelette complet à comprendre ligne par ligne :
#!/bin/bash
echo "=== État du système ==="
echo "Utilisateur : $(whoami)"
echo "Machine : $(hostname)"
echo "Répertoire : $(pwd)"
FICHIER="/tmp/${1:-rapport.txt}" # variable + valeur par défaut
touch "$FICHIER"
if [ $? -eq 0 ]; then
echo "Fichier créé : $FICHIER"
else
echo "Échec de création" >&2
exit 1
fi
ls -la "$FICHIER"
Décodage des nouveautés, une par une :
FICHIER="...": une variable interne du script. Convention : MAJUSCULES pour les variables « de configuration ».${1}: le premier paramètre passé au script../etat.sh monfichier.txtrend"monfichier.txt"disponible en$1. La forme${1:-rapport.txt}dit : « prends$1, ourapport.txtsi$1est vide ». Votre outil gagne une option gratuite.[ $? -eq 0 ]: le test. Il relit le code de retour de la commande juste avant (icitouch).-eqcompare numériquement (« equal »). ⚠️ Les espaces autour des crochets et autour de-eqsont obligatoires :[est réellement une commande, pas un symbole décoratif.[ $? -eq 0]sans espace final est une erreur de syntaxe.if ... then ... else ... fi: la structure conditionnelle du shell. Notezfi(= if à rebours) pour fermer.>&2: redirige la sortie de cette ligne vers stderr (canal 2). Le message d'échec part donc dans le canal des diagnostics — cohérent avec la leçon 1-05.exit 1: le script choisit son code de retour. C'est la boucle est bouclée avec la leçon 2-03 : votre script devient consommable par d'autres scripts (./etat.sh && echo "tout va bien").
Lancer et observer
$ chmod +x etat.sh
$ ./etat.sh
=== État du système ===
Utilisateur : cours
Machine : cours
Répertoire : /home/cours
Fichier créé : /tmp/rapport.txt
-rw-r--r-- 1 cours cours 0 Jan 12 11:05 /tmp/rapport.txt
$ echo $?
0
$ ./etat.sh essai-perso.txt
...
Fichier créé : /tmp/essai-perso.txt
Prenez le temps de relire chaque sortie et de la relier à la ligne de script correspondante. Ce va-et-vient script ↔ sortie est le meilleur exercice de lecture shell qui existe.
Exercice
- Créez
etat.shdans votre répertoire de travail, avec le comportement suivant :- afficher utilisateur, machine et répertoire courant (une information par ligne, format « Libellé : valeur ») ;
- créer un fichier dans
/tmpdont le nom vient de$1, avec valeur par défaut si absent ; - vérifier le code de retour de la création et afficher « création ok » ou « échec de la création » (sur stderr pour l'échec) ;
- se terminer avec le code 0 en cas de succès, 1 en cas d'échec.
- Rendez-le exécutable, lancez-le sans argument, puis avec un nom personnalisé.
- Vérifiez son code de sortie avec
echo $?dans les deux cas. - Relancez-le via
bash etat.shaprès avoir retiré le droit d'exécution (chmod -x etat.sh) : expliquez pourquoi ça marche encore, et ce qui ne marche plus. - Enrichissement libre : ajoutez une ligne affichant la date, et une autre comptant les processus
bashen cours (ps aux | grep bash | wc -l).
Contraintes
- Aucune écriture hors de
/tmpet de votre répertoire personnel. - Une information par ligne, lisible sans décodeur.
- Le script doit rester correct même lancé deux fois de suite (réflexion : pourquoi
>serait-il un mauvais choix dans un script ?).
Indices
Sur $? : il est réécrit par chaque commande. Si vous voulez tester le résultat de touch, le if [ $? -eq 0 ] doit venir immédiatement après — tout echo intermédiaire écraserait la valeur.
**Sur ${1:-defaut} :** cette syntaxe se lit « paramètre 1, sinon defaut ». Testez-la depuis le terminal d'abord : echo "${1:-vide}"`.
Sur le débogage : ajoutez temporairement set -x en deuxième ligne du script : bash affichera chaque commande exécutée avec ses valeurs substituées. Retirez-le ensuite. C'est l'outil de débogage shell le plus rentable qui existe — gardez-le en tête pour toujours.
Correction disponibleCherchez d’abord par vous-même.Voir la correction
Correction
Solution complète
#!/bin/bash
echo "=== Rapport d'état ==="
echo "Utilisateur : $(whoami)"
echo "Machine : $(hostname)"
echo "Répertoire : $(pwd)"
echo "Date : $(date)"
NOM_FICHIER="${1:-etat-rapport.txt}"
CIBLE="/tmp/$NOM_FICHIER"
touch "$CIBLE"
if [ $? -eq 0 ]; then
echo "Création ok : $CIBLE"
else
echo "Échec de la création" >&2
exit 1
fi
Mise en route
$ nano etat.sh # coller le contenu, enregistrer
$ chmod +x etat.sh
$ ./etat.sh
=== Rapport d'état ===
Utilisateur : cours
Machine : cours
Répertoire : /home/cours
Date : dim. 12 janv. 2026 11:12:40 CET
Création ok : /tmp/etat-rapport.txt
$ ./etat.sh rapport-special.txt
...
Création ok : /tmp/rapport-special.txt
$ echo $?
0
Explication des choix
- Variables nommées (
NOM_FICHIER,CIBLE) plutôt que répétition de${1:-...}: le script se relit et se modifie en un seul endroit. Première leçon implicite de qualité de code. "$CIBLE"quoté partout : si le nom de fichier contenait un espace, les guillemets empêchent le découpage. Réflexe permanent en shell : quoter ses variables.- Le
ifsuit immédiatementtouch: le code testé est bien celui de la création. exit 1sur l'échec : sans lui, le script rendrait 0 même en cas de problème (le code de sortie d'un script = celui de sa dernière commande). Notre outil mentrait à ses futurs appelants.
Question 4 : pourquoi bash etat.sh marche sans x
Parce que dans ce cas, ce n'est pas le fichier qu'on exécute : c'est bash, qui reçoit le nom du fichier en simple argument et le lit comme du texte. Le droit x ne sert que pour le lancement direct ./etat.sh. Ce qui disparaît alors : la décision automatique de l'interpréteur via le shebang — si le fichier commence par #!/bin/sh et que votre bash n'est pas sh strict, des comportements peuvent différer. Le shebang garantit l'interpréteur ; l'appel explicite impose le vôtre.
Votre premier script est terminé : il lit son environnement, agit, vérifie et annonce honnêtement son résultat. C'est exactement le contrat que respecteront tous les outils que vous construirez dans ce cursus — y compris vos serveurs Node.js, dès le prochain chapitre.