Retour au cours

signaux trap

Objectifs

  • Comprendre ce qu’est un signal et connaître les signaux les plus courants (SIGINT, SIGTERM).
  • Intercepter ces signaux avec la commande trap pour exécuter une action spécifique.
  • Créer des scripts robustes qui effectuent des opérations de nettoyage (ex: supprimer des fichiers temporaires) avant de se terminer, même en cas d’interruption.

Explications détaillées

Notions clés

Les Signaux : Messages du Système

Un signal est une notification envoyée par le système d’exploitation à un processus pour l’informer d’un événement. Un script peut choisir d’ignorer un signal, d’adopter le comportement par défaut, ou d’exécuter une action personnalisée.

Signaux courants :

  • SIGINT (Signal Interrupt, N°2) : Envoyé lorsque vous appuyez sur Ctrl+C. L’action par défaut est de terminer le processus.
  • SIGTERM (Signal Terminate, N°15) : La manière “propre” de demander à un processus de s’arrêter. C’est le signal envoyé par défaut par la commande kill.
  • SIGKILL (Signal Kill, N°9) : Le signal “ultime”. Il ne peut jamais être intercepté ou ignoré. Le système tue le processus de force. C’est le kill -9.
  • SIGHUP (Signal Hang Up, N°1) : Envoyé quand le terminal est fermé.
  • EXIT : Ce n’est pas un vrai signal, mais un événement spécial de Bash qui se déclenche à la fin du script, quelle qu’en soit la raison (fin normale, exit, ou un signal).

trap : Le piège à signaux

La commande intégrée trap permet à votre script d’exécuter du code lorsqu’un ou plusieurs signaux sont reçus. C’est le mécanisme de gestion d’événements de Bash.

Syntaxe de base : trap 'ma_commande' SIGNAL1 SIGNAL2 ...

Le cas d’usage le plus important de trap est de garantir que les opérations de nettoyage sont toujours exécutées.

Syntaxe / Usages

# Exécute la commande 'echo' quand le script reçoit SIGINT (Ctrl+C)
trap 'echo " - Interruption détectée !"' SIGINT

# Exécute une fonction 'cleanup' à la fin du script (normale ou non)
trap cleanup EXIT

# Fonction de nettoyage classique
cleanup() {
  echo "Nettoyage des ressources..."
  rm -f /tmp/mon_fichier_temporaire.txt
}

# Ignorer un signal (en passant une chaîne vide)
trap '' SIGINT # Ctrl+C est maintenant ignoré

# Réinitialiser le comportement par défaut
trap - SIGINT

Exemples

# 1. Intercepter Ctrl+C pour un message personnalisé
trap 'echo -e "\nArrêt demandé. Au revoir."; exit' SIGINT

echo "Le script tourne... Appuyez sur Ctrl+C pour arrêter proprement."
# Une boucle infinie pour maintenir le script en vie
while true; do
  sleep 1
done

# 2. Le motif de nettoyage le plus robuste
# Le trap est défini au début du script.
cleanup() {
  echo "--- Exécution du nettoyage ---"
  # On supprime le fichier temporaire s'il existe
  rm -f "$TMP_FILE"
  echo "Nettoyage terminé."
}
trap cleanup EXIT

# Le script crée un fichier temporaire
TMP_FILE=$(mktemp)
echo "Fichier temporaire créé : $TMP_FILE"

echo "Le script travaille..."
echo "Simulons une erreur en plein milieu..."
# exit 1 # Décommentez pour voir le trap s'exécuter
sleep 10
echo "Le script se termine normalement."

Lancez ce deuxième script. Vous verrez que cleanup est appelé à la fin. Maintenant, lancez-le à nouveau et faites Ctrl+C pendant le sleep. Vous verrez que cleanup est quand même appelé avant que le script ne se termine.

Bonnes pratiques

  • Le trap cleanup EXIT est votre meilleur ami. Définissez une fonction de nettoyage et liez-la au pseudo-signal EXIT. Cela garantit qu’elle sera appelée dans presque toutes les situations, rendant vos scripts beaucoup plus propres et plus sûrs.
  • Gardez votre fonction de nettoyage simple et idempotente (on peut l’exécuter plusieurs fois sans causer de problème). Par exemple, utilisez rm -f qui ne produit pas d’erreur si le fichier n’existe pas.
  • Déclarez vos trap au début du script pour qu’ils soient actifs le plus tôt possible.

Pièges courants

  • Tenter d’intercepter SIGKILL : C’est impossible. trap '...' SIGKILL sera ignoré. kill -9 terminera toujours votre script de force.
  • Complexité dans la chaîne de trap : Mettre des commandes longues et complexes directement dans la chaîne du trap (trap '...beaucoup de code...' EXIT) est une mauvaise pratique. Utilisez toujours une fonction.
  • Les signaux et les subshells : Un trap défini dans un script principal n’est pas hérité par les subshells (ex: dans un pipeline |).

Exercices

  1. Le script poli :

    • Écrivez un script qui entre dans une boucle while true avec un sleep 1.
    • Ajoutez un trap qui, sur SIGINT (Ctrl+C), affiche “Non, pas comme ça ! Utilisez Ctrl+\ pour quitter.” et continue la boucle.
    • Ajoutez un second trap qui, sur SIGQUIT (Ctrl+\), affiche “Ok, vous insistez. Au revoir.” et quitte le script.
  2. Nettoyage multi-fichiers :

    • Créez un script qui crée trois fichiers temporaires différents (/tmp/file1, /tmp/file2, etc.).
    • Écrivez une fonction de nettoyage qui tente de supprimer ces trois fichiers.
    • Liez cette fonction à EXIT avec trap.
    • Ajoutez un sleep 20 pour avoir le temps de l’interrompre.
    • Lancez le script, interrompez-le, et vérifiez que les fichiers temporaires ont bien été supprimés.
  3. Désactivation temporaire :

    • Dans un script, mettez en place un trap qui affiche un message sur SIGINT.
    • Au milieu du script, pour une section de code “critique”, désactivez temporairement le trap (trap - SIGINT), puis réactivez-le après.
    • Testez en faisant Ctrl+C pendant la section critique et en dehors.