Module 5 : Introduction au Shell Scripting
Objectifs du Module
À l'issue de ce module, vous serez capable de :
- Comprendre le fonctionnement du shell Bash
- Écrire des scripts avec variables, conditions et boucles
- Passer et traiter des arguments
- Créer des fonctions réutilisables
- Débugger vos scripts
Durée : 8 heures
Niveau : Débutant
1. Le Shell Bash
Qu'est-ce qu'un Shell ?
Le shell est l'interface entre l'utilisateur et le système d'exploitation. Bash (Bourne Again SHell) est le shell le plus répandu.
graph TD
U[Utilisateur] -->|Commande| S[Shell Bash]
S -->|Appel système| K[Kernel Linux]
K -->|Résultat| S
S -->|Affichage| U
style S fill:#4CAF50,stroke:#2E7D32,color:#fff
style K fill:#2196F3,stroke:#1565C0,color:#fff
Vérifier votre Shell
# Shell actuel
echo $SHELL
# /bin/bash
# Version de Bash
bash --version
# GNU bash, version 5.1.8(1)-release
# Shells disponibles
cat /etc/shells
# /bin/sh
# /bin/bash
# /bin/zsh
2. Premier Script
Structure de Base
Le Shebang (#!)
La première ligne #!/bin/bash indique quel interpréteur utiliser :
| Shebang | Interpréteur |
|---|---|
#!/bin/bash |
Bash |
#!/bin/sh |
Shell POSIX (portable) |
#!/usr/bin/env bash |
Bash (cherche dans PATH) |
#!/usr/bin/python3 |
Python 3 |
Créer et Exécuter un Script
# 1. Créer le script
cat > hello.sh << 'EOF'
#!/bin/bash
echo "Hello, World!"
echo "Date: $(date)"
echo "User: $USER"
EOF
# 2. Rendre exécutable
chmod +x hello.sh
# 3. Exécuter
./hello.sh
# Hello, World!
# Date: Fri Nov 29 14:30:00 CET 2024
# User: alice
# Alternatives d'exécution
bash hello.sh # Sans chmod +x
source hello.sh # Dans le shell courant
. hello.sh # Identique à source
3. Variables
Définir et Utiliser
# Définir (pas d'espace autour du =)
nom="Alice"
age=25
chemin="/home/alice"
# Utiliser (avec $)
echo "Nom: $nom"
echo "Age: $age"
echo "Home: $chemin"
# Accolades (recommandé)
echo "Bonjour ${nom}!"
echo "Fichier: ${chemin}/documents"
Variables Spéciales
| Variable | Description |
|---|---|
$0 |
Nom du script |
$1, $2... |
Arguments positionnels |
$# |
Nombre d'arguments |
$@ |
Tous les arguments (séparés) |
$* |
Tous les arguments (une chaîne) |
$? |
Code retour de la dernière commande |
$$ |
PID du script |
$! |
PID du dernier processus en arrière-plan |
#!/bin/bash
echo "Script: $0"
echo "Premier argument: $1"
echo "Nombre d'arguments: $#"
echo "Tous les arguments: $@"
echo "PID: $$"
Variables d'Environnement
# Afficher toutes les variables d'environnement
env
printenv
# Variables courantes
echo $USER # Utilisateur courant
echo $HOME # Répertoire home
echo $PATH # Chemins des exécutables
echo $PWD # Répertoire courant
echo $SHELL # Shell par défaut
echo $HOSTNAME # Nom de la machine
# Exporter une variable
export MA_VAR="valeur"
# Variable pour les sous-processus uniquement
(export LOCAL_VAR="test"; echo $LOCAL_VAR)
Substitution de Commande
# Syntaxe moderne (recommandée)
date_actuelle=$(date +%Y-%m-%d)
fichiers=$(ls -1 | wc -l)
# Ancienne syntaxe (backticks)
date_actuelle=`date +%Y-%m-%d`
# Utilisation
echo "Date: $date_actuelle"
echo "Nombre de fichiers: $fichiers"
# Imbrication
chemin_complet=$(dirname $(readlink -f $0))
Lecture Interactive
#!/bin/bash
# Lecture simple
echo -n "Entrez votre nom: "
read nom
echo "Bonjour, $nom!"
# Avec prompt intégré
read -p "Entrez votre âge: " age
# Lecture silencieuse (mot de passe)
read -sp "Mot de passe: " password
echo "" # Nouvelle ligne
# Avec timeout
read -t 5 -p "Répondez en 5 secondes: " reponse
# Valeur par défaut
read -p "Continuer? [O/n] " choix
choix=${choix:-O} # O si vide
4. Conditions
Test de Base
# Syntaxe avec [ ]
if [ condition ]; then
commandes
fi
# Syntaxe avec [[ ]] (Bash moderne, recommandée)
if [[ condition ]]; then
commandes
fi
# if / else
if [[ condition ]]; then
commandes_si_vrai
else
commandes_si_faux
fi
# if / elif / else
if [[ condition1 ]]; then
commandes1
elif [[ condition2 ]]; then
commandes2
else
commandes_default
fi
Opérateurs de Comparaison
Chaînes de Caractères
| Opérateur | Description |
|---|---|
= ou == |
Égal |
!= |
Différent |
-z |
Chaîne vide |
-n |
Chaîne non vide |
< |
Inférieur (ordre alphabétique) |
> |
Supérieur |
nom="Alice"
if [[ "$nom" == "Alice" ]]; then
echo "Bonjour Alice!"
fi
if [[ -z "$variable_vide" ]]; then
echo "La variable est vide"
fi
if [[ -n "$nom" ]]; then
echo "Le nom est défini"
fi
Nombres
| Opérateur | Description |
|---|---|
-eq |
Égal (equal) |
-ne |
Différent (not equal) |
-lt |
Inférieur (less than) |
-le |
Inférieur ou égal |
-gt |
Supérieur (greater than) |
-ge |
Supérieur ou égal |
age=25
if [[ $age -ge 18 ]]; then
echo "Majeur"
else
echo "Mineur"
fi
# Avec (( )) pour l'arithmétique (pas de $)
if (( age >= 18 )); then
echo "Majeur"
fi
Fichiers
| Opérateur | Description |
|---|---|
-e |
Existe |
-f |
Est un fichier régulier |
-d |
Est un répertoire |
-r |
Est lisible |
-w |
Est modifiable |
-x |
Est exécutable |
-s |
Taille > 0 |
-L |
Est un lien symbolique |
fichier="/etc/passwd"
if [[ -f "$fichier" ]]; then
echo "$fichier existe"
fi
if [[ -d "/home/alice" ]]; then
echo "Le répertoire existe"
fi
if [[ -r "$fichier" && -w "$fichier" ]]; then
echo "Fichier lisible et modifiable"
fi
Opérateurs Logiques
| Opérateur | Description |
|---|---|
&& |
ET logique |
|| |
OU logique |
! |
NON logique |
age=25
nom="Alice"
if [[ $age -ge 18 && "$nom" == "Alice" ]]; then
echo "Alice est majeure"
fi
if [[ $age -lt 18 || "$nom" == "Bob" ]]; then
echo "Mineur OU Bob"
fi
if [[ ! -f "/fichier" ]]; then
echo "Le fichier n'existe pas"
fi
case - Switch/Case
#!/bin/bash
read -p "Entrez une commande (start/stop/restart): " cmd
case $cmd in
start)
echo "Démarrage..."
;;
stop)
echo "Arrêt..."
;;
restart)
echo "Redémarrage..."
;;
*)
echo "Commande inconnue: $cmd"
exit 1
;;
esac
5. Boucles
for - Itération sur Liste
# Liste explicite
for fruit in pomme orange banane; do
echo "Fruit: $fruit"
done
# Fichiers
for fichier in *.txt; do
echo "Traitement de $fichier"
done
# Séquence numérique
for i in {1..5}; do
echo "Nombre: $i"
done
# Avec pas
for i in {0..10..2}; do
echo "Pair: $i"
done
# Style C
for ((i=0; i<5; i++)); do
echo "Index: $i"
done
# Arguments du script
for arg in "$@"; do
echo "Argument: $arg"
done
while - Tant Que
# Compteur
compteur=0
while [[ $compteur -lt 5 ]]; do
echo "Compteur: $compteur"
((compteur++))
done
# Lecture de fichier ligne par ligne
while IFS= read -r ligne; do
echo "Ligne: $ligne"
done < fichier.txt
# Boucle infinie avec condition de sortie
while true; do
read -p "Continuer? (o/n) " reponse
if [[ "$reponse" == "n" ]]; then
break
fi
done
until - Jusqu'à Ce Que
break et continue
# break - sortir de la boucle
for i in {1..10}; do
if [[ $i -eq 5 ]]; then
break
fi
echo $i
done
# Affiche: 1 2 3 4
# continue - passer à l'itération suivante
for i in {1..5}; do
if [[ $i -eq 3 ]]; then
continue
fi
echo $i
done
# Affiche: 1 2 4 5
6. Fonctions
Définition et Appel
#!/bin/bash
# Définition
saluer() {
echo "Bonjour, $1!"
}
# Appel
saluer "Alice"
saluer "Bob"
# Avec return
est_pair() {
local nombre=$1
if (( nombre % 2 == 0 )); then
return 0 # Vrai
else
return 1 # Faux
fi
}
# Utilisation
if est_pair 42; then
echo "42 est pair"
fi
Variables Locales
ma_fonction() {
local var_locale="Je suis locale"
var_globale="Je suis globale"
echo "$var_locale"
}
ma_fonction
echo "$var_locale" # Vide (locale à la fonction)
echo "$var_globale" # Affiche: Je suis globale
Retourner des Valeurs
# Méthode 1 : echo (capture avec $())
calculer_double() {
local nombre=$1
echo $((nombre * 2))
}
resultat=$(calculer_double 21)
echo "Double: $resultat" # 42
# Méthode 2 : Variable globale
calculer_triple() {
local nombre=$1
RESULTAT=$((nombre * 3))
}
calculer_triple 14
echo "Triple: $RESULTAT" # 42
Exemple Complet
#!/bin/bash
# Script avec fonctions
# Fonction de log
log() {
local niveau=$1
shift
local message="$*"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$niveau] $message"
}
# Fonction de vérification fichier
verifier_fichier() {
local fichier=$1
if [[ ! -f "$fichier" ]]; then
log "ERROR" "Fichier non trouvé: $fichier"
return 1
fi
if [[ ! -r "$fichier" ]]; then
log "ERROR" "Fichier non lisible: $fichier"
return 1
fi
log "INFO" "Fichier OK: $fichier"
return 0
}
# Main
main() {
log "INFO" "Démarrage du script"
for fichier in "$@"; do
verifier_fichier "$fichier"
done
log "INFO" "Fin du script"
}
# Exécution
main "$@"
7. Gestion des Erreurs
Codes de Retour
# Vérifier le code retour
commande
if [[ $? -eq 0 ]]; then
echo "Succès"
else
echo "Échec"
fi
# Syntaxe concise
commande && echo "Succès" || echo "Échec"
# Personnaliser le code de sortie
if [[ ! -f "$fichier" ]]; then
echo "Erreur: fichier non trouvé" >&2
exit 1
fi
Options de Sécurité
#!/bin/bash
set -euo pipefail
# -e : Arrêter en cas d'erreur
# -u : Erreur si variable non définie
# -o pipefail : Propager les erreurs dans les pipes
Trap - Gestion des Signaux
#!/bin/bash
# Nettoyer à la sortie
cleanup() {
echo "Nettoyage..."
rm -f /tmp/script_$$_*
}
# Attraper EXIT, SIGINT (Ctrl+C), SIGTERM
trap cleanup EXIT
# Script principal
echo "Travail en cours..."
touch /tmp/script_$$_temp
sleep 10
echo "Terminé"
8. Debugging
Options de Debug
# Exécuter en mode debug
bash -x script.sh
# Activer/désactiver dans le script
set -x # Activer le debug
# ... code ...
set +x # Désactiver le debug
# Mode verbose
bash -v script.sh
# Combinaison
bash -xv script.sh
Techniques de Debug
#!/bin/bash
# Afficher les variables
DEBUG=${DEBUG:-false}
debug() {
if [[ "$DEBUG" == "true" ]]; then
echo "[DEBUG] $*" >&2
fi
}
variable="test"
debug "variable = $variable"
# Exécution
# Normal: ./script.sh
# Debug: DEBUG=true ./script.sh
9. Exercice Pratique
Exercice : Script de Sauvegarde
Objectif : Créer un script de sauvegarde de répertoires.
Fonctionnalités :
- Accepter un répertoire source en argument
- Créer une archive
.tar.gzavec la date dans le nom - Sauvegarder dans
/backup(ou un répertoire spécifié) - Vérifier que la source existe
- Afficher un résumé (taille, emplacement)
- Gérer les erreurs proprement
Usage attendu :
Durée estimée : 45 minutes
10. Solution
Solution Détaillée
#!/bin/bash
#
# backup.sh - Script de sauvegarde
# Usage: backup.sh <source> [destination]
#
set -euo pipefail
# === Configuration ===
readonly SCRIPT_NAME=$(basename "$0")
readonly DATE=$(date +%Y%m%d_%H%M%S)
readonly DEFAULT_DEST="/backup"
# === Fonctions ===
usage() {
cat << EOF
Usage: $SCRIPT_NAME <source> [destination]
Arguments:
source Répertoire à sauvegarder
destination Répertoire de destination (défaut: $DEFAULT_DEST)
Exemple:
$SCRIPT_NAME /home/alice/documents
$SCRIPT_NAME /etc /backup/configs
EOF
exit 1
}
log() {
local niveau=$1
shift
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$niveau] $*"
}
error() {
log "ERROR" "$*" >&2
exit 1
}
verifier_source() {
local source=$1
if [[ ! -e "$source" ]]; then
error "Source non trouvée: $source"
fi
if [[ ! -r "$source" ]]; then
error "Source non lisible: $source"
fi
log "INFO" "Source vérifiée: $source"
}
preparer_destination() {
local dest=$1
if [[ ! -d "$dest" ]]; then
log "INFO" "Création du répertoire: $dest"
mkdir -p "$dest" || error "Impossible de créer $dest"
fi
if [[ ! -w "$dest" ]]; then
error "Destination non accessible en écriture: $dest"
fi
log "INFO" "Destination prête: $dest"
}
creer_backup() {
local source=$1
local dest=$2
local nom_base=$(basename "$source")
local archive="${dest}/${nom_base}_${DATE}.tar.gz"
log "INFO" "Création de l'archive: $archive"
if tar -czf "$archive" -C "$(dirname "$source")" "$nom_base"; then
log "INFO" "Archive créée avec succès"
echo "$archive"
else
error "Échec de la création de l'archive"
fi
}
afficher_resume() {
local archive=$1
local taille=$(du -h "$archive" | cut -f1)
echo ""
echo "==================================="
echo " Sauvegarde Terminée "
echo "==================================="
echo "Archive: $archive"
echo "Taille: $taille"
echo "Date: $(date)"
echo "==================================="
}
# === Main ===
main() {
# Vérifier les arguments
if [[ $# -lt 1 ]]; then
usage
fi
local source=$1
local dest=${2:-$DEFAULT_DEST}
log "INFO" "Démarrage de la sauvegarde"
log "INFO" "Source: $source"
log "INFO" "Destination: $dest"
# Vérifications
verifier_source "$source"
preparer_destination "$dest"
# Créer le backup
local archive
archive=$(creer_backup "$source" "$dest")
# Résumé
afficher_resume "$archive"
log "INFO" "Sauvegarde terminée avec succès"
}
# Exécution
main "$@"
Test du Script
# Rendre exécutable
chmod +x backup.sh
# Créer un répertoire de test
mkdir -p ~/test_backup/documents
echo "Fichier test" > ~/test_backup/documents/test.txt
# Créer le répertoire de destination
sudo mkdir -p /backup
sudo chown $USER:$USER /backup
# Exécuter
./backup.sh ~/test_backup/documents
# Vérifier
ls -la /backup/
tar -tzf /backup/documents_*.tar.gz
Points Clés à Retenir
| Concept | Syntaxe |
|---|---|
| Shebang | #!/bin/bash |
| Variable | nom="valeur", $nom, ${nom} |
| Substitution | $(commande) |
| Condition | if [[ condition ]]; then ... fi |
| Boucle for | for i in liste; do ... done |
| Boucle while | while [[ cond ]]; do ... done |
| Fonction | nom() { ... } |
| Variable locale | local var="valeur" |
| Code retour | $?, exit 1 |
| Mode strict | set -euo pipefail |
Félicitations !
Vous avez terminé le Niveau 1 - Fondations Linux !
Vous maîtrisez maintenant :
- L'installation et la découverte de Linux
- La navigation et manipulation de fichiers
- La gestion des utilisateurs et permissions
- Les éditeurs et le traitement de texte
- Les bases du scripting Shell
Prochaine étape : Le Niveau 2 - Administration Système vous attend !
Module 6 : Gestion des Paquets
Retour au : Programme de la Formation
Navigation
| ← Module 4 : Éditeurs & Traitement de T... | Module 6 : Gestion des Paquets → |