(Inspirado pela resposta de Gilles)
Com o ISIGsinalizador definido, a única maneira de obter o Childscript SIGINTsem que seu pai obtenha SIGINTé estar em seu próprio grupo de processos. Isso pode ser realizado com a set -mopção
Se você ativar a -mopção no Childscript 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 SIGINTquando o INTRcaractere for lido.
Aqui está a descrição POSIX da -mopção :
-mEsta 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 -mopção é semelhante a -i, mas não altera o comportamento do shell quase tanto quanto o -ifaz.
Exemplo:
o 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"
o 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"
É o que acontece quando você pressiona Control+ Cenquanto Childaguarda 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 SIGINTmanipulador do pai nunca é executado.
Como alternativa, se você preferir modificar em Parentvez de Child, pode fazer o seguinte:
o 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"
o Childscript (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
SIGINTpela duração de Child. Isso não aborda sua pergunta, mas pode ser o que você deseja.
- Modifique
Childpara:
- Use
stty -gpara fazer backup das configurações atuais do terminal.
- Corra
stty -isigpara não gerar sinais com os INTR, QUITe SUSPcaracteres.
- Em segundo plano, leia a entrada do terminal e envie os sinais conforme apropriado (por exemplo, execute
kill -QUIT 0quando 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 Childscript 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 setpgidutilitá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 erroSIGQUITeSIGTERMé ignorado.