web-dev-qa-db-fra.com

Gardez les codes de sortie lors du piégeant SIGINT et similaires?

Si j'utilise trap comme décrit par ex. sur http://linuxcommand.org/wss0160.php#trap Pour attraper Ctrl-C (ou similaire) et le nettoyage avant de quitter, je modifie le code de sortie renvoyé.

Maintenant, cela ne fera probablement pas la différence dans le monde réel (par exemple, parce que les codes de sortie ne sont pas portables et qui ne sont pas toujours sans ambiguïtés comme indiqués dans code de sortie par défaut lorsque le processus est terminé? ) mais toujours Je me demande s'il n'y a vraiment aucun moyen de l'empêcher et de renvoyer le code d'erreur par défaut pour les scripts interrompus à la place?

Exemple (à bash, mais ma question ne devrait pas être considérée comme spécifique au Bash):

#!/bin/bash
trap 'echo EXIT;' EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. ' _
trap 'echo SIGINT; exit 1;' INT
read -p 'If you ctrl-c me now my return code will be 1. ' _

Sortir:

$ ./test.sh # doing ctrl-c for 1st read
If you ctrl-c me now my return code will be the default for SIGTERM.
$ echo $?
130
$ ./test.sh # doing ctrl-c for 2nd read
If you ctrl-c me now my return code will be the default for SIGTERM.
If you ctrl-c me now my return code will be 1. SIGINT 
EXIT
$ echo $?
1

(Édité pour enlever pour le rendre plus conforme à Posix.)

(Modifié à nouveau pour en faire un script Bash à la place, ma question n'est pas spécifique au shell.)

Édité pour utiliser le portable "INT" pour le piège en faveur du "SIGINT" non portable.

Édité pour éliminer les accolades bouclés inutiles et ajouter une solution potentielle.

Mettre à jour:

Je l'ai résolu maintenant en sortant simplement avec des codes d'erreur codes à code de sortie et la sortie de piégeage. Cela pourrait être problématique sur certains systèmes car le code d'erreur peut différer ou le piège de sortie n'est pas possible, mais dans mon cas, c'est assez bien.

trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
14
phk

Tout ce que vous avez à faire est de modifier le gestionnaire de sortie à l'intérieur votre gestionnaire de nettoyage. Voici un exemple:

#!/bin/bash
cleanup() {
    echo trapped exit
    trap 'exit 0' EXIT
}
trap cleanup EXIT
read -p 'If you ctrl-c me now my return code will be the default for SIGTERM. '
4
rocky

En fait, l'interruption de Bash's interne read semble être un peu différent pour interrompre une commande exécutée par Bash. Normalement, lorsque vous entrez trap, $? est défini et vous pouvez la préserver et sortir avec la même valeur:

trap 'rc=$?; echo $rc SIGINT; exit $rc' INT
trap 'rc=$?; echo $rc EXIT; exit $rc' EXIT

Si votre script est interrompu lors de l'exécution d'une commande comme sleep ou même une intégrée comme wait, vous verrez

130 SIGINT
130 EXIT

et le code de sortie est de 130. Cependant, pour read -p, il semble $? est 0 (sur ma version de Bash 4.3.42 de toute façon).


Le traitement des signaux pendant read peut fonctionner en cours, selon le fichier de modifications de ma version ... (/ USR/Share/Doc/Bash/Modifications)

changements entre cette version, Bash-4.3-Alpha et la version précédente, Bash-4.2-Release.

  1. Nouvelles fonctionnalités à Bash

    r. En mode POSIX, "LIRE" est interruptible par un signal piégé. Après avoir exécuté le gestionnaire de piège, lisez le retour de 128 + et jette une entrée partiellement lue.

9
meuh

Tout code de sortie de signal habituel sera disponible en $? Lors de l'entrée du gestionnaire de piège:

sig_handler() {
    exit_status=$?  # Eg 130 for SIGINT, 128 + (2 == SIGINT)
    echo "Doing signal-specific up"
    exit "$exit_status"
}
trap sig_handler INT HUP TERM QUIT

S'il y a un piège de sortie séparé, vous pouvez utiliser la même méthodologie: maintenez l'état de la sortie passée du gestionnaire de signal (s'il y en a un) nettoyage, puis renvoyez l'état de sortie enregistré.

6
Tom Hale

Retournez simplement que le code d'erreur ne suffit pas pour simuler une sortie par SIGINT. Je suis surpris que personne n'en ait mentionné cela jusqu'à présent. En plus de lecture: https://www.cons.org/cracauer/sigint.html

La manière appropriée est:

for sig in EXIT ABRT HUP INT PIPE QUIT TERM; do
    trap "cleanup;
          [ $sig  = EXIT ] && normal_exit_only_cleanup;
          [ $sig != EXIT ] && trap - $sig EXIT && kill -s $sig $$
         " $sig
done

Cela fonctionne avec Bash, Dash et ZSH. Pour plus de portabilité, vous devez utiliser des spécifications de signal numérique (d'autre part, ZSH attend un paramètre de chaîne pour la commande kill ...)

Notez également le traitement spécial du signal EXIT. Cela est dû à certaines obus (nommément bash) exécutant des pièges sur EXIT pour tout signal également (après des pièges éventuellement définis sur ce signal). La réinitialisation du piège EXIT empêche cela.

Exécution du code uniquement sur la sortie "normale"

Le cheque [ $sig = EXIT ] permet d'exécuter le code uniquement sur la sortie normale (non signalée). Cependant, Tous les signaux doivent avoir des pièges qui réinitialisent enfin le piège sur EXIT alors; normal_exit_only_cleanup sera également appelé aux signaux qui ne le font pas. Il sera également exécuté par une sortie à travers set -e. Cela peut être corrigé en piégeant sur ERR (qui n'est pas pris en charge par DASH) et en ajoutant un chèque [ $sig = ERR ] avant le kill.

Version simplifiée Bash uniquement

D'autre part, ce comportement signifie que dans Bash, vous pouvez simplement faire

trap cleanup EXIT

pour simplement exécuter un code de nettoyage et conserver le statut de sortie.

édité

  • Le comportement de Bash élaborate de "sortie piège tout"

  • Supprimer le signal de tuer, qui ne peut pas être piégé

  • Supprimer les préfixes SIG des noms de signaux

  • N'essayez pas de kill -s EXIT

  • Prendre set -e/Commission d'erreur

2
philipp2100