(Inspiré par la réponse de Gilles)
Avec l' ISIGindicateur défini, le seul moyen pour le Childscript d'obtenir SIGINTsans obtenir son parent SIGINTest qu'il se trouve dans son propre groupe de processus. Cela peut être accompli avec l' set -moption.
Si vous activez l' -moption dans le Childscript shell, il effectuera le contrôle des travaux sans être interactif. Cela entraînera son exécution dans un groupe de processus distinct, empêchant le parent de recevoir le SIGINTlorsque le INTRcaractère est lu.
Voici la description POSIX de l' -moption :
-mCette option doit être prise en charge si la mise en œuvre prend en charge l'option User Portability Utilities. Tous les travaux doivent être exécutés dans leurs propres groupes de processus. Immédiatement avant que le shell émette une invite après la fin de la tâche en arrière-plan, un message signalant l'état de sortie de la tâche en arrière-plan doit être écrit sur l'erreur standard. Si un travail de premier plan s'arrête, le shell doit écrire un message d'erreur standard à cet effet, formaté comme décrit par l'utilitaire de travaux. En outre, si un travail change d'état autre que la sortie (par exemple, s'il s'arrête pour une entrée ou une sortie ou est arrêté par un signal SIGSTOP), le shell doit écrire un message similaire immédiatement avant d'écrire l'invite suivante. Cette option est activée par défaut pour les shells interactifs.
L' -moption est similaire à -i, mais elle ne modifie pas autant le comportement du shell -i.
Exemple:
le Parentscript:
#!/bin/sh
trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
echo "PARENT: pid=$$"
echo "PARENT: Spawning child..."
./Child
echo "PARENT: child returned"
echo "PARENT: exiting normally"
le Childscript:
#!/bin/sh -m
# ^^
# notice the -m option above!
trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
echo "CHILD: pid=$$"
echo "CHILD: hit enter to exit"
read foo
echo "CHILD: exiting normally"
Voici ce qui se passe lorsque vous appuyez sur Control+ Cen Childattendant l'entrée:
$ ./Parent
PARENT: pid=12233
PARENT: Spawning child...
CHILD: pid=12234
CHILD: hit enter to exit
^CCHILD: caught SIGINT; exiting
PARENT: child returned
PARENT: exiting normally
Remarquez comment le SIGINTgestionnaire du parent n'est jamais exécuté.
Alternativement, si vous préférez modifier Parentau lieu de Child, vous pouvez le faire:
le Parentscript:
#!/bin/sh
trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
echo "PARENT: pid=$$"
echo "PARENT: Spawning child..."
sh -m ./Child # or 'sh -m -c ./Child' if Child isn't a shell script
echo "PARENT: child returned"
echo "PARENT: exiting normally"
le Childscript (normal; pas besoin de -m):
#!/bin/sh
trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
echo "CHILD: pid=$$"
echo "CHILD: hit enter to exit"
read foo
echo "CHILD: exiting normally"
Idées alternatives
- Modifiez les autres processus du groupe de processus de premier plan à ignorer
SIGINTpendant la durée de Child. Cela ne répond pas à votre question, mais cela peut vous donner ce que vous voulez.
- Modifiez
Childen:
- Utilisez
stty -gpour sauvegarder les paramètres actuels du terminal.
- Exécuter
stty -isigpour ne pas générer des signaux avec les INTR, QUITet les SUSPpersonnages.
- En arrière-plan, lisez l'entrée du terminal et envoyez les signaux vous-même selon le cas (par exemple, exécutez
kill -QUIT 0lorsque Control+ \est lu, kill -INT $$lorsque Control+ Cest lu). Ce n'est pas anodin et il peut ne pas être possible de le faire fonctionner correctement si le Childscript ou tout ce qu'il exécute est censé être interactif.
- Restaurez les paramètres du terminal avant de quitter (idéalement à partir d'un piège
EXIT).
- Identique à # 2, sauf qu'au lieu de courir
stty -isig, attendez que l'utilisateur frappe Enterou une autre clé non spéciale avant de tuer Child.
Écrivez votre propre setpgidutilitaire en C, Python, Perl, etc. que vous pouvez utiliser pour appeler setpgid(). Voici une implémentation C brute:
#define _XOPEN_SOURCE 700
#include <unistd.h>
#include <signal.h>
int
main(int argc, char *argv[])
{
// todo: add error checking
void (*backup)(int);
setpgid(0, 0);
backup = signal(SIGTTOU, SIG_IGN);
tcsetpgrp(0, getpid());
signal(SIGTTOU, backup);
execvp(argv[1], argv + 1);
return 1;
}
Exemple d'utilisation de Child:
#!/bin/sh
[ "${DID_SETPGID}" = true ] || {
# restart self after calling setpgid(0, 0)
exec env DID_SETPGID=true setpgid "$0" "$@"
# exec failed if control reached this point
exit 1
}
unset DID_SETPGID
# do stuff here
ksh). Exemples:${ENV}provient, le shell ne se fermera pas immédiatement lorsqu'il rencontre une erreur,SIGQUITetSIGTERMest ignoré.