10 jeux amusants à jouer dans le terminal Linux
Agence web » Actualités du digital » Comment utiliser les signaux Linux dans les scripts Bash

Comment utiliser les signaux Linux dans les scripts Bash

Le noyau Linux envoie des signaux aux processus sur les événements auxquels ils doivent réagir. Les scripts bien comportés gèrent les signaux avec élégance et robustesse et peuvent nettoyer derrière eux même si vous appuyez sur Ctrl+C. Voici comment.

Signaux et processus

Les signaux sont des messages courts, rapides et unidirectionnels envoyés à des processus tels que des scripts, des programmes et des démons. Ils informent le processus de quelque chose qui s’est passé. L’utilisateur a peut-être appuyé sur Ctrl+C, ou l’application a peut-être essayé d’écrire dans la mémoire à laquelle elle n’a pas accès.

Si l’auteur du processus a prévu qu’un certain signal pourrait lui être envoyé, il peut écrire une routine dans le programme ou le script pour gérer ce signal. Une telle routine s’appelle un gestionnaire de signal. Il capte ou piège le signal et exécute une action en réponse à celui-ci.

Linux utilise beaucoup de signaux, comme nous le verrons, mais du point de vue des scripts, il n’y a qu’un petit sous-ensemble de signaux qui pourrait vous intéresser. En particulier, dans les scripts non triviaux, les signaux qui indiquent au le script à arrêter doit être piégé (si possible) et un arrêt progressif doit être effectué.

Par exemple, les scripts qui créent des fichiers temporaires ou ouvrent des ports de pare-feu peuvent avoir la possibilité de supprimer les fichiers temporaires ou de fermer les ports avant qu’ils ne s’arrêtent. Si le script meurt juste à l’instant où il reçoit le signal, votre ordinateur peut être laissé dans un état imprévisible.

Voici comment gérer les signaux dans vos propres scripts.

Rencontrez les signaux

Certaines commandes Linux ont des noms cryptés. Ce n’est pas le cas de la commande qui piège les signaux. C’est appelé trap. Nous pouvons également utiliser trap avec le -l (liste) pour nous montrer la liste complète des signaux utilisés par Linux.

trap -l

Lister les signaux dans Ubuntu avec trap -l

Bien que notre liste numérotée se termine à 64, il y a en fait 62 signaux. Les signaux 32 et 33 sont manquants. Ils ne sont pas implémentés sous Linux. Ils ont été remplacés par des fonctionnalités dans le gcc compilateur pour gérer les threads en temps réel. Tout depuis le signal 34, SIGRTMINau signal 64, SIGRTMAXsont des signaux en temps réel.

Vous verrez différentes listes sur différents systèmes d’exploitation de type Unix. Sur OpenIndiana par exemple, les signaux 32 et 33 sont présents, ainsi qu’un tas de signaux supplémentaires portant le nombre total à 73.

Lister les signaux dans OpenIndiana avec trap -l

Les signaux peuvent être référencés par nom, numéro ou par leur nom abrégé. Leur nom abrégé est simplement leur nom avec le premier « SIG » supprimé.

Les signaux sont émis pour de nombreuses raisons différentes. Si vous pouvez les déchiffrer, leur but est contenu dans leur nom. L’impact d’un signal appartient à l’une des catégories suivantes :

  • Mettre fin: Le processus est terminé.
  • Ignorer: Le signal n’affecte pas le processus. Il s’agit d’un signal d’information uniquement.
  • Cœur: Un fichier dump-core est créé. Cela se produit généralement parce que le processus a transgressé d’une manière ou d’une autre, comme une violation de mémoire.
  • Arrêt: Le processus est arrêté. C’est-à-dire que c’est en pausenon terminé.
  • Continuer: Indique à un processus arrêté de continuer son exécution.

Ce sont les signaux que vous rencontrerez le plus fréquemment.

  • SIGHUP: Signal 1. La connexion à un hôte distant, tel qu’un serveur SSH, a été interrompue de manière inattendue ou l’utilisateur s’est déconnecté. Un script recevant ce signal peut se terminer normalement ou peut choisir de tenter de se reconnecter à l’hôte distant.
  • SIGINT: Signal 2. L’utilisateur a appuyé sur la combinaison Ctrl+C pour forcer la fermeture d’un processus, ou kill La commande a été utilisée avec le signal 2. Techniquement, il s’agit d’un signal d’interruption, pas d’un signal de fin, mais un script interrompu sans gestionnaire de signal se terminera généralement.
  • SIGQUITTER: Signal 3. L’utilisateur a appuyé sur la combinaison Ctrl+D pour forcer un processus à quitter, ou le kill commande a été utilisée avec le signal 3.
  • SIGFPE: Signal 8. Le processus a tenté d’effectuer une opération mathématique illégale (impossible), telle qu’une division par zéro.
  • SIGKILL: Signal 9. C’est le signal équivalent d’une guillotine. Vous ne pouvez pas l’attraper ou l’ignorer, et cela se produit instantanément. Le processus se termine immédiatement.
  • SIGTERME: Signal 15. C’est la version la plus prévenante de SIGKILL. SIGTERM indique également à un processus de se terminer, mais il peut être piégé et le processus peut exécuter ses processus de nettoyage avant de se fermer. Cela permet un arrêt en douceur. C’est le signal par défaut émis par le kill commande.

Signaux sur la ligne de commande

Une façon de piéger un signal est d’utiliser trap avec le numéro ou le nom du signal, et une réponse que vous voulez qu’il se produise si le signal est reçu. Nous pouvons le démontrer dans une fenêtre de terminal.

Cette commande piège le SIGINT signal. La réponse est d’imprimer une ligne de texte dans la fenêtre du terminal. Nous utilisons le -e (activer les échappements) option avec echo donc on peut utiliser le « n” spécificateur de format.

trap 'echo -e "+c Detected."' SIGINT

Intercepter Ctrl+C sur la ligne de commande

Notre ligne de texte est imprimée chaque fois que nous appuyons sur la combinaison Ctrl+C.

Pour voir si un piège est défini sur un signal, utilisez le -p (piège d’impression).

trap -p SIGINT

Vérifier si un piège est défini sur un signal

Utilisant trap sans options fait la même chose.

Pour réinitialiser le signal à son état normal non piégé, utilisez un trait d’union « -” et le nom du signal piégé.

trap - SIGINT
trap -p SIGINT

Suppression d'un déroutement d'un signal

Aucune sortie du trap -p La commande indique qu’aucun piège n’est défini sur ce signal.

Piéger les signaux dans les scripts

Nous pouvons utiliser le même format général trap commande dans un script. Ce script piège trois signaux différents, SIGINT, SIGQUITet SIGTERM.

#!/bin/bash

trap "echo I was SIGINT terminated; exit" SIGINT
trap "echo I was SIGQUIT terminated; exit" SIGQUIT
trap "echo I was SIGTERM terminated; exit" SIGTERM

echo $$
counter=0

while true
do 
  echo "Loop number:" $((++counter))
  sleep 1
done

Les trois trap les instructions sont en haut du script. Notez que nous avons inclus le exit commande à l’intérieur de la réponse à chacun des signaux. Cela signifie que le script réagit au signal puis se termine.

Copiez le texte dans votre éditeur et enregistrez-le dans un fichier appelé « simple-loop.sh », et rendez-le exécutable en utilisant le chmod commande. Vous devrez le faire pour tous les scripts de cet article si vous souhaitez suivre sur votre propre ordinateur. Utilisez simplement le nom du script approprié dans chaque cas.

chmod +x simple-loop.sh

Rendre un script exécutable avec chmod

Le reste du script est très simple. Nous avons besoin de connaître l’ID de processus du script, nous avons donc le script qui nous l’écho. La $$ La variable contient l’ID de processus du script.

Nous créons une variable appelée counter et le mettre à zéro.

La while la boucle s’exécutera indéfiniment à moins qu’elle ne soit arrêtée de force. Il incrémente le counter variable, la renvoie à l’écran et s’endort une seconde.

Exécutons le script et envoyons-lui différents signaux.

./simple-loop.sh

Un script l'identifiant a été terminé avec Ctrl+C

Lorsque nous appuyons sur « Ctrl + C », notre message est imprimé dans la fenêtre du terminal et le script est terminé.

Exécutons-le à nouveau et envoyons le SIGQUIT signaler à l’aide du kill commande. Nous devrons le faire à partir d’une autre fenêtre de terminal. Vous devrez utiliser l’ID de processus signalé par votre propre script.

./simple-loop.sh
kill -SIGQUIT 4575

Un script l'identifiant a été terminé avec SIGQUIT

Comme prévu, le script signale l’arrivée du signal puis se termine. Et enfin, pour prouver le point, nous allons le refaire avec le SIGTERM signal.

./simple-loop.sh
kill -SIGTERM 4584

Un script l'identifiant a été terminé avec SIGTERM

Nous avons vérifié que nous pouvons piéger plusieurs signaux dans un script et réagir à chacun indépendamment. L’étape qui fait passer tout cela d’intéressant à utile consiste à ajouter des gestionnaires de signaux.

Gestion des signaux dans les scripts

Nous pouvons remplacer la chaîne de réponse par le nom d’une fonction dans votre script. La trap La commande appelle ensuite cette fonction lorsque le signal est détecté.

Copiez ce texte dans un éditeur et enregistrez-le dans un fichier appelé « grace.sh », et rendez-le exécutable avec chmod.

#!/bin/bash

trap graceful_shutdown SIGINT SIGQUIT SIGTERM

graceful_shutdown()
{
  echo -e "nRemoving temporary file:" $temp_file
  rm -rf "$temp_file"
  exit
}

temp_file=$(mktemp -p /tmp tmp.XXXXXXXXXX)
echo "Created temp file:" $temp_file

counter=0

while true
do 
  echo "Loop number:" $((++counter))
  sleep 1
done

Le script place un piège pour trois signaux différents— SIGHUP, SIGINTet SIGTERM—à l’aide d’un seul trap déclaration. La réponse est le nom du graceful_shutdown() fonction. La fonction est appelée chaque fois qu’un des trois signaux piégés est reçu.

Le script crée un fichier temporaire dans le répertoire « /tmp », en utilisant mktemp. Le modèle de nom de fichier est « tmp.XXXXXXXXXX », donc le nom du fichier sera « tmp ». suivi de dix caractères alphanumériques aléatoires. Le nom du fichier s’affiche en écho à l’écran.

Le reste du script est le même que le précédent, avec un counter variable et un infini while boucle.

./grace.sh

Un script effectuant un arrêt progressif en supprimant un fichier temporaire

Lorsque le fichier reçoit un signal qui provoque sa fermeture, le graceful_shutdown() fonction est appelée. Cela supprime notre unique fichier temporaire. Dans une situation réelle, il pourrait effectuer tout le nettoyage requis par votre script.

De plus, nous avons regroupé tous nos signaux piégés et les avons traités avec une seule fonction. Vous pouvez piéger les signaux individuellement et les envoyer à leurs propres fonctions de gestion dédiées.

Copiez ce texte et enregistrez-le dans un fichier appelé « triple.sh », et rendez-le exécutable à l’aide du chmod commande.

#!/bin/bash

trap sigint_handler SIGINT
trap sigusr1_handler SIGUSR1
trap exit_handler EXIT

function sigint_handler() {
  ((++sigint_count))

  echo -e "nSIGINT received $sigint_count time(s)."

  if [[ "$sigint_count" -eq 3 ]]; then
    echo "Starting close-down."
    loop_flag=1
  fi
}

function sigusr1_handler() {
  echo "SIGUSR1 sent and received $((++sigusr1_count)) time(s)."
}

function exit_handler() { 
  echo "Exit handler: Script is closing down..."
}

echo $$
sigusr1_count=0
sigint_count=0
loop_flag=0

while [[ $loop_flag -eq 0 ]]; do
  kill -SIGUSR1 $$
  sleep 1
done

Nous définissons trois pièges en haut du script.

  • Un piège SIGINT et a un gestionnaire appelé sigint_handler().
  • Le second piège un signal appelé SIGUSR1 et utilise un gestionnaire appelé sigusr1_handler() .
  • Le piège numéro trois piège le EXIT signal. Ce signal est déclenché par le script lui-même lorsqu’il se ferme. Définition d’un gestionnaire de signal pour EXIT signifie que vous pouvez définir une fonction qui sera toujours appelée à la fin du script (à moins qu’elle ne soit tuée avec le signal SIGKILL). Notre gestionnaire s’appelle exit_handler() .

SIGUSR1 et SIGUSR2 sont des signaux fournis afin que vous puissiez envoyer des signaux personnalisés à vos scripts. La façon dont vous les interprétez et y réagissez dépend entièrement de vous.

Laissant de côté les gestionnaires de signaux pour le moment, le corps du script devrait vous être familier. Il renvoie l’ID de processus à la fenêtre du terminal et crée des variables. Variable sigusr1_count enregistre le nombre de fois SIGUSR1 a été traité, et sigint_count enregistre le nombre de fois SIGINT a été manipulé. La loop_flag la variable est mise à zéro.

La while boucle n’est pas une boucle infinie. Il s’arrêtera de boucler si le loop_flag variable est définie sur n’importe quelle valeur différente de zéro. Chaque rotation du while boucle utilise kill pour envoyer le SIGUSR1 signal à ce script, en l’envoyant à l’ID de processus du script. Les scripts peuvent s’envoyer des signaux à eux-mêmes !

La sigusr1_handler() fonction incrémente le sigusr1_count variable et envoie un message à la fenêtre du terminal.

A chaque fois le SIGINT signal est reçu, le siguint_handler() fonction incrémente le sigint_count variable et renvoie sa valeur à la fenêtre du terminal.

Si la sigint_count variable est égale à trois, la loop_flag est définie sur un et un message est envoyé à la fenêtre du terminal informant l’utilisateur que le processus d’arrêt a commencé.

Car loop_flag n’est plus égal à zéro, le while boucle se termine et le script est terminé. Mais cette action augmente automatiquement le EXIT signal et le exit_handler() fonction est appelée.

./triple.sh

Un script utilisant SIGUSR1, nécessitant trois combinaisons Ctrl + C pour se fermer et captant le signal EXIT à l'arrêt

Après trois appuis sur Ctrl+C, le script se termine et invoque automatiquement le exit_handler() fonction.

Lire les signaux

En piégeant les signaux et en les traitant dans des fonctions de gestion simples, vous pouvez faire en sorte que vos scripts Bash se rangent derrière eux même s’ils se terminent de manière inattendue. Cela vous donne un système de fichiers plus propre. Cela empêche également l’instabilité la prochaine fois que vous exécutez le script et, selon l’objectif de votre script, cela pourrait même empêcher les failles de sécurité.

★★★★★