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
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. '
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.
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.
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é.
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.
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
.
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.
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