avancé · Durée estimée : 45 min
Passer du mode "je tape des commandes une par une" au mode "j'automatise des tâches". Tu vas apprendre à écrire des scripts shell qui enchaînent des commandes, prennent des décisions et répètent des actions.
Le shell scripting est la compétence la plus rentable de l'administration Linux. Un script de 20 lignes peut remplacer une heure de travail manuel.
Ce tutoriel utilise /bin/sh (POSIX, portable)
mais tout fonctionne aussi avec bash.
Un script shell est un simple fichier texte contenant une suite de commandes. La première ligne, appelée shebang, indique quel interpréteur utiliser :
#!/bin/sh
ou, pour Bash spécifiquement :
#!/bin/bash
#!/bin/sh
# Ceci est un commentaire
echo "Bonjour"
echo "Il est $(date +%H:%M)"
chmod +x mon_script.sh
./mon_script.sh
./ est nécessaire : sans lui, le shell
cherche mon_script.sh dans le PATH,
pas dans le dossier courant.
bonjour.sh, rends-le exécutable et
lance-le.
Bonjour et l'heure.
Une variable stocke une valeur. En shell, pas de type : tout est une chaîne de caractères (ou un nombre, selon l'usage).
nom="Alice"
age=30
echo "Bonjour $nom"
echo "Tu as $age ans"
echo "Bonjour ${nom}, tu as ${age} ans"
=.
nom = "Alice" ne fonctionne pas.
nom="Alice" oui.
fichier="notes"
echo "$fichier.txt" # → ".txt" (la variable fichier.txt n'existe pas)
echo "${fichier}.txt" # → "notes.txt"
$0 → nom du script
$1, $2 → arguments passés au script
$# → nombre d'arguments
$@ → tous les arguments
$? → code retour de la dernière commande
$$ → PID du script
$USER → utilisateur courant
$HOME → dossier personnel
$PWD → dossier courant
a=5
b=3
somme=$((a + b))
echo "Somme : $somme"
echo "Multiplication : $((a * b))"
echo "Division entière : $((a / b))"
$(( )) est réservé aux calculs entiers.
Pour des décimaux, il faudra bc ou
awk.
calc.sh qui définit
a=12 et b=4, puis affiche
leur somme, différence, produit et quotient.
read#!/bin/sh
echo -n "Ton prénom : "
read prenom
echo "Bonjour $prenom !"
read -p "Ton âge : " age
echo "Tu as $age ans"
read -p "Prénom et nom : " prenom nom
echo "Prénom : $prenom / Nom : $nom"
read -s -p "Mot de passe : " mdp
echo
echo "Mot de passe reçu (${#mdp} caractères)"
${#mdp} donne la longueur d'une chaîne.
Utile pour valider une saisie sans afficher son contenu.
ifif [ condition ]; then
# instructions si vrai
fi
if [ "$age" -lt 18 ]; then
echo "Mineur"
elif [ "$age" -lt 65 ]; then
echo "Majeur actif"
else
echo "Senior"
fi
-eq égal à
-ne différent de
-lt inférieur à
-le inférieur ou égal
-gt supérieur à
-ge supérieur ou égal
= égal
!= différent
-z chaîne vide
-n chaîne non vide
-e fichier → existe
-f fichier → existe et est un fichier normal
-d fichier → existe et est un dossier
-r fichier → lisible
-w fichier → modifiable
-x fichier → exécutable
-s fichier → existe et non vide
if [ -f /etc/passwd ]; then
echo "Fichier présent"
fi
if [ ! -d /tmp/mondossier ]; then
echo "Le dossier n'existe pas, je le crée"
mkdir /tmp/mondossier
fi
if [ "$USER" = "root" ]; then
echo "Attention : tu es root !"
fi
[ ... ] sont
obligatoires : [ "$a" = "$b" ]
fonctionne, ["$a"="$b"] ne fonctionne pas.
[ est en réalité une commande.
if [ "$age" -gt 18 ] && [ "$age" -lt 65 ]; then
echo "Actif"
fi
if [ "$nom" = "Alice" ] || [ "$nom" = "Bob" ]; then
echo "Bienvenue"
fi
[[ ... ]]
(Bash uniquement) qui gère mieux les espaces et les regex.
Mais [ ... ] est plus portable.
for et whilefor fruit in pomme banane cerise; do
echo "J'aime la $fruit"
done
for i in 1 2 3 4 5; do
echo "Tour n°$i"
done
for i in $(seq 1 10); do
echo "$i"
done
for f in *.txt; do
echo "Fichier trouvé : $f"
done
compteur=1
while [ $compteur -le 5 ]; do
echo "Compteur : $compteur"
compteur=$((compteur + 1))
done
while true; do
echo -n "Tape 'quit' pour sortir : "
read reponse
if [ "$reponse" = "quit" ]; then
break
fi
echo "Tu as tapé : $reponse"
done
compteur=1
until [ $compteur -gt 5 ]; do
echo "Compteur : $compteur"
compteur=$((compteur + 1))
done
until = "jusqu'à ce que". Inverse de
while : la boucle tourne tant que la
condition est fausse.
Un script peut recevoir des arguments en ligne de commande :
./mon_script.sh arg1 arg2 arg3
#!/bin/sh
echo "Nom du script : $0"
echo "Premier argument : $1"
echo "Deuxième argument : $2"
echo "Nombre d'arguments : $#"
echo "Tous les arguments : $@"
#!/bin/sh
if [ $# -eq 0 ]; then
echo "Usage : $0 <nom>"
exit 1
fi
echo "Bonjour $1"
exit 1 termine le script avec un code
d'erreur. exit 0 est un succès. Un script
bien fait renvoie toujours un code significatif.
salut.sh qui prend un prénom en
argument et affiche "Bonjour PRÉNOM". S'il n'y a pas
d'argument, il affiche un message d'usage.
./salut.sh Alice → "Bonjour Alice"../salut.sh → message d'usage.
Une fonction regroupe des commandes réutilisables :
#!/bin/sh
dire_bonjour() {
echo "Bonjour $1"
}
dire_bonjour "Alice"
dire_bonjour "Bob"
addition() {
echo $(( $1 + $2 ))
}
resultat=$(addition 3 5)
echo "3 + 5 = $resultat"
est_pair() {
if [ $(($1 % 2)) -eq 0 ]; then
return 0 # vrai
else
return 1 # faux
fi
}
if est_pair 4; then
echo "4 est pair"
fi
echo (capturé via $( ))
return
carre qui prend un nombre
et affiche son carré. Teste avec 3, 5 et 7.
set -e et ||#!/bin/sh
set -e
cd /dossier/inexistant # erreur → script s'arrête
echo "Jamais atteint"
||mkdir /tmp/test || echo "Échec création"
mkdir /tmp/test || { echo "Échec"; exit 1; }
&&mkdir /tmp/test && cd /tmp/test && echo "OK"
rm /inexistant 2>/dev/null
set -e en haut d'un script est une bonne
pratique : ton script s'arrête dès qu'une commande échoue,
au lieu de continuer dans un état incohérent.
set -e qui tente de
cd /dossier/inexistant puis affiche un
message. Le message ne doit pas s'afficher.
cd et ne
va pas plus loin.
Un script complet qui combine tout ce qu'on a vu :
#!/bin/sh
set -e
# Variables
DOSSIER_SOURCE="${1:-$HOME/projet}"
DOSSIER_DEST="${2:-$HOME/backups}"
DATE=$(date +%Y-%m-%d_%H-%M-%S)
# Vérifications
if [ ! -d "$DOSSIER_SOURCE" ]; then
echo "Erreur : $DOSSIER_SOURCE n'existe pas"
exit 1
fi
mkdir -p "$DOSSIER_DEST"
# Création de l'archive
NOM_ARCHIVE="backup_${DATE}.tar.gz"
echo "Création de $NOM_ARCHIVE..."
tar -czf "$DOSSIER_DEST/$NOM_ARCHIVE" -C "$(dirname "$DOSSIER_SOURCE")" "$(basename "$DOSSIER_SOURCE")"
# Vérification
if [ -f "$DOSSIER_DEST/$NOM_ARCHIVE" ]; then
TAILLE=$(du -h "$DOSSIER_DEST/$NOM_ARCHIVE" | cut -f1)
echo "✅ Sauvegarde réussie : $NOM_ARCHIVE ($TAILLE)"
else
echo "❌ Échec de la sauvegarde"
exit 1
fi
Utilisation :
chmod +x backup.sh
./backup.sh # utilise les valeurs par défaut
./backup.sh /etc /tmp/sauvegardes # dossiers personnalisés
${1:-valeur})tar${1:-défaut} signifie : "utilise $1 s'il est
défini, sinon utilise la valeur par défaut". C'est
extrêmement pratique pour les scripts avec options.
~/projet
créé dans le tutoriel 08.
~/backups/.
#!/bin/sh → shebang
chmod +x script.sh → rendre exécutable
./script.sh → lancer
nom="Alice" → affecter
$nom, ${nom} → utiliser
$1, $2, $# → arguments
$?, $$ → code retour, PID
$((a + b)) → calcul
read var → lire dans var
read -p "prompt" var → avec invite
read -s var → silencieux
if [ cond ]; then ... fi
if [ cond ]; then ... else ... fi
if [ cond ]; then ... elif ... else ... fi
-eq -ne -lt -le -gt -ge → numériques
= != -z -n → chaînes
-e -f -d -r -w -x → fichiers
for i in liste; do ... done
for i in $(seq 1 10); do ... done
while [ cond ]; do ... done
until [ cond ]; do ... done
break / continue
nom() { commandes; }
resultat=$(nom arg)
set -e → arrêt à la 1re erreur
"$variable" → toujours entre guillemets
exit 0 / exit 1 → code de sortie explicite
${1:-défaut} → valeur par défaut
Crée un script menu.sh qui affiche un menu,
demande un choix, et exécute l'action correspondante.
#!/bin/sh
while true; do
echo ""
echo "=== MENU ==="
echo "1) Afficher la date"
echo "2) Afficher qui je suis"
echo "3) Lister les fichiers"
echo "4) Quitter"
echo -n "Choix : "
read choix
case "$choix" in
1) date ;;
2) whoami ;;
3) ls -la ;;
4) echo "Au revoir !"; exit 0 ;;
*) echo "Choix invalide" ;;
esac
done
casecase "$variable" in
motif1) commandes ;;
motif2) commandes ;;
*) commandes par défaut ;;
esac
case est parfait pour les menus. Plus
lisible qu'une longue chaîne de if/elif.
Ajoute ces fonctionnalités au menu :
$HOME/projetdf -h)/usr/bin/ sur n'importe quel système Linux.
tableau=(a b c); echo ${tableau[0]}=~ dans
[[ ]]$(commande) vs `commande`diff <(cmd1) <(cmd2)apk add shellcheck)
shellcheck est ton meilleur ami. Il détecte
les erreurs que tu ne verras qu'à l'exécution.