(Inspirado pela resposta de Gilles)
Com o ISIG
sinalizador definido, a única maneira de obter o Child
script SIGINT
sem que seu pai obtenha SIGINT
é estar em seu próprio grupo de processos. Isso pode ser realizado com a set -m
opção
Se você ativar a -m
opção no Child
script de shell, ele executará o controle do trabalho sem ser interativo. Isso fará com que ele execute coisas em um grupo de processos separado, impedindo que o pai receba SIGINT
quando o INTR
caractere for lido.
Aqui está a descrição POSIX da -m
opção :
-m
Esta opção será suportada se a implementação suportar a opção Utilitários de Portabilidade do Usuário. Todos os trabalhos devem ser executados em seus próprios grupos de processos. Imediatamente antes do shell emitir um prompt após a conclusão do trabalho em segundo plano, uma mensagem relatando o status de saída do trabalho em segundo plano deve ser gravada com erro padrão. Se um trabalho em primeiro plano parar, o shell gravará uma mensagem de erro padrão nesse sentido, formatado conforme descrito pelo utilitário de trabalhos. Além disso, se um trabalho alterar um status diferente de sair (por exemplo, se parar para entrada ou saída ou for interrompido por um sinal SIGSTOP), o shell deve escrever uma mensagem semelhante imediatamente antes de escrever o próximo prompt. Esta opção está ativada por padrão para shells interativos.
A -m
opção é semelhante a -i
, mas não altera o comportamento do shell quase tanto quanto o -i
faz.
Exemplo:
o Parent
script:
#!/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"
o Child
script:
#!/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"
É o que acontece quando você pressiona Control+ Cenquanto Child
aguarda a 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 como o SIGINT
manipulador do pai nunca é executado.
Como alternativa, se você preferir modificar em Parent
vez de Child
, pode fazer o seguinte:
o Parent
script:
#!/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"
o Child
script (normal; sem necessidade -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éias alternativas
- Modifique os outros processos no grupo de processos em primeiro plano para ignorar
SIGINT
pela duração de Child
. Isso não aborda sua pergunta, mas pode ser o que você deseja.
- Modifique
Child
para:
- Use
stty -g
para fazer backup das configurações atuais do terminal.
- Corra
stty -isig
para não gerar sinais com os INTR
, QUIT
e SUSP
caracteres.
- Em segundo plano, leia a entrada do terminal e envie os sinais conforme apropriado (por exemplo, execute
kill -QUIT 0
quando Control+ \for lido, kill -INT $$
quando Control+ Cfor lido). Isso não é trivial e pode não ser possível fazer com que isso funcione sem problemas, se o Child
script ou qualquer coisa que ele executa for interativo.
- Restaure as configurações do terminal antes de sair (idealmente a partir de uma armadilha
EXIT
).
- Igual ao número 2, exceto em vez de executar
stty -isig
, aguarde o usuário pressionar Enterou alguma outra chave não especial antes de matar Child
.
Escreva seu próprio setpgid
utilitário em C, Python, Perl, etc., que você pode usar para chamar setpgid()
. Aqui está uma implementação grosseira 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;
}
Exemplo 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
). Exemplos:${ENV}
é originário, o shell não sai imediatamente quando encontra um erroSIGQUIT
eSIGTERM
é ignorado.