(Inspirado por la respuesta de Gilles)
Con el ISIG
indicador establecido, la única forma de que el Child
script se obtenga SIGINT
sin que su padre lo obtenga SIGINT
es que esté en su propio grupo de procesos. Esto se puede lograr con la set -m
opción.
Si activa la -m
opción en el Child
script de shell, realizará el control del trabajo sin ser interactivo. Esto hará que ejecute cosas en un grupo de proceso separado, evitando que el padre reciba el SIGINT
cuando INTR
se lee el personaje.
Aquí está la descripción POSIX de la -m
opción :
-m
Esta opción se admitirá si la implementación admite la opción Utilidades de portabilidad del usuario. Todos los trabajos se ejecutarán en sus propios grupos de procesos. Inmediatamente antes de que el shell emita un aviso después de completar el trabajo en segundo plano, se escribirá un mensaje de error que informe el estado de salida del trabajo en segundo plano. Si se detiene un trabajo en primer plano, el shell escribirá un mensaje de error estándar a ese efecto, formateado como se describe en la utilidad de trabajos. Además, si un trabajo cambia de estado además de salir (por ejemplo, si se detiene para entrada o salida o es detenido por una señal SIGSTOP), el shell debe escribir un mensaje similar inmediatamente antes de escribir el siguiente mensaje. Esta opción está habilitada de forma predeterminada para shells interactivos.
La -m
opción es similar a -i
, pero no altera el comportamiento del shell tanto como lo -i
hace.
Ejemplo:
el Parent
guión:
#!/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"
el Child
guión:
#!/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"
Esto es lo que sucede cuando presiona Control+ Cmientras Child
espera la entrada:
$ ./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
Observe cómo el controlador del padre SIGINT
nunca se ejecuta.
Alternativamente, si prefiere modificar en Parent
lugar de Child
, puede hacer esto:
el Parent
guión:
#!/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"
el Child
script (normal; no es necesario -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"
Ideas alternativas
- Modifique los otros procesos en el grupo de procesos en primer plano para ignorarlos
SIGINT
durante la duración de Child
. Esto no responde a su pregunta, pero puede obtener lo que desea.
- Modificar
Child
a:
- Use
stty -g
para hacer una copia de seguridad de la configuración actual del terminal.
- Correr
stty -isig
para no generar señales con las INTR
, QUIT
y SUSP
caracteres.
- En el fondo, leer la entrada del terminal y enviar las señales a sí mismo como apropiado (por ejemplo, correr
kill -QUIT 0
cuando Control+ \se lee, kill -INT $$
cuando Control+ Cse lee). Esto no es trivial, y puede que no sea posible hacer que funcione sin problemas si el Child
script o cualquier cosa que ejecute esté destinado a ser interactivo.
- Restaure la configuración del terminal antes de salir (idealmente desde una trampa activada
EXIT
).
- Igual que el # 2, excepto que en lugar de ejecutarse
stty -isig
, espere a que el usuario presione Entero alguna otra tecla no especial antes de matar Child
.
Escriba su propia setpgid
utilidad en C, Python, Perl, etc. que pueda usar para llamar setpgid()
. Aquí hay una implementación cruda de C:
#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;
}
Ejemplo de uso 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
). Ejemplos:${ENV}
es de origen, el shell no se cerrará inmediatamente cuando encuentre un errorSIGQUIT
ySIGTERM
se ignorará.