(Inspiriert von Gilles 'Antwort)
Wenn das ISIG
Flag gesetzt ist, kann das Child
Skript nur dann abrufen, wenn es SIGINT
übergeordnet SIGINT
ist, wenn es sich in einer eigenen Prozessgruppe befindet. Dies kann mit der set -m
Option erreicht werden.
Wenn Sie die -m
Option im Child
Shell-Skript aktivieren, wird die Jobsteuerung ausgeführt, ohne interaktiv zu sein. Dies führt dazu, dass Dinge in einer separaten Prozessgruppe ausgeführt werden, wodurch verhindert wird, dass das übergeordnete Element das empfängt, SIGINT
wenn das INTR
Zeichen gelesen wird.
Hier ist die POSIX-Beschreibung der -m
Option :
-m
Diese Option wird unterstützt, wenn die Implementierung die Option User Portability Utilities unterstützt. Alle Jobs sollen in eigenen Prozessgruppen ausgeführt werden. Unmittelbar bevor die Shell nach Abschluss des Hintergrundjobs eine Eingabeaufforderung ausgibt, wird eine Nachricht, die den Beendigungsstatus des Hintergrundjobs meldet, in den Standardfehler geschrieben. Wenn ein Vordergrundjob angehalten wird, schreibt die Shell eine entsprechende Nachricht mit Standardfehler, die wie vom Jobdienstprogramm beschrieben formatiert ist. Wenn ein Job einen anderen Status als das Beenden ändert (z. B. wenn er für die Eingabe oder Ausgabe stoppt oder durch ein SIGSTOP-Signal gestoppt wird), muss die Shell unmittelbar vor dem Schreiben der nächsten Eingabeaufforderung eine ähnliche Nachricht schreiben. Diese Option ist standardmäßig für interaktive Shells aktiviert.
Die -m
Option ähnelt -i
, ändert jedoch das Verhalten der Shell nicht annähernd so stark wie -i
sie.
Beispiel:
das Parent
Skript:
#!/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"
das Child
Skript:
#!/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"
Dies passiert, wenn Sie Control+ drücken, Cwährend Sie Child
auf die Eingabe warten:
$ ./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
Beachten Sie, dass der SIGINT
Handler des Elternteils niemals ausgeführt wird.
Wenn Sie Parent
stattdessen lieber ändern möchten Child
, können Sie dies alternativ tun:
das Parent
Skript:
#!/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"
das Child
Skript (normal; keine Notwendigkeit -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"
Alternative Ideen
- Ändern Sie die anderen Prozesse in der Vordergrundprozessgruppe, um sie
SIGINT
für die Dauer von zu ignorieren Child
. Damit wird Ihre Frage nicht beantwortet, aber Sie erhalten möglicherweise das, was Sie möchten.
- Ändern
Child
zu:
- Verwenden Sie
stty -g
diese Option, um die aktuellen Terminaleinstellungen zu sichern.
- Führen Sie
stty -isig
nicht erzeugen Signale mit den INTR
, QUIT
und SUSP
Zeichen.
- Lesen Sie im Hintergrund den Terminaleingang und senden Sie die Signale gegebenenfalls selbst (z. B. ausführen,
kill -QUIT 0
wenn Control+ \gelesen wird, kill -INT $$
wenn Control+ Cgelesen wird). Dies ist nicht trivial und es ist möglicherweise nicht möglich, dass dies reibungslos funktioniert, wenn das Child
Skript oder etwas, das es ausführt, interaktiv sein soll.
- Stellen Sie die Terminaleinstellungen vor dem Beenden wieder her (idealerweise ab einer Falle
EXIT
).
- Wie # 2 mit der Ausnahme , anstatt läuft
stty -isig
, Warten auf den Benutzer Hit Enteroder einem anderen Nicht-Spezialschlüssel vor der Tötung Child
.
Schreiben Sie Ihr eigenes setpgid
Dienstprogramm in C, Python, Perl usw., das Sie zum Aufrufen verwenden können setpgid()
. Hier ist eine grobe C-Implementierung:
#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;
}
Anwendungsbeispiel von 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
). Beispiele:${ENV}
Wird bezogen, wird die Shell nicht sofort beendet, wenn ein Fehler auftritt,SIGQUIT
undSIGTERM
wird ignoriert.