O que está acontecendo
Quando você pressiona Ctrl + C , o sinal SIGINT
é entregue ao conjunto grupo de processos em primeiro plano . Aqui ele é enviado para o processo find
e o processo de shell de chamada. find
reage saindo imediatamente, e a casca reage chamando a armadilha.
Se o código no trap retorna (ou seja, não chama exit
), a execução prossegue com o comando após o que foi interrompido pelo sinal. Aqui, depois que o comando find
chegar ao final do script, o script sairá imediatamente assim mesmo. Mas você pode ver a diferença entre inserir 0 e 1 adicionando outro comando:
find /
echo "find returned $?"
Uma maneira de fazer o que você quer (mas provavelmente não deveria fazer)
Você pode fazer o que quiser; mas esta parte da minha resposta é mais sobre como descobrir programação de shell do que resolver um problema real.
- Como uma questão de design, um sinal reinicializável não é o que você normalmente esperaria no tipo de programas relativamente simples que normalmente são os shell scripts. A expectativa é que Ctrl + C mate o script.
- Como você verá abaixo, ele amplia os recursos do shell de qualquer maneira.
Se você quiser evitar a eliminação de find
, será necessário iniciá-lo no plano de fundo : %código%. Em seguida, use o find / &
builtin para que ele saia normalmente. Um sinal interromperá o wait
embutido, que você pode executar em um loop até receber um sinal que deseja propagar. Em seguida, use wait
para matar o trabalho.
hell () {
echo "Do you want to quit? Press 1 for yes and 0 for no"
read n
if [ "$n" = 1 ]; then
# Kill the job if it's running, then exit
if [ -n "$job_pid" ]; then kill $job_pid; fi
exit 1
fi
}
job_pid=
trap "hell" SIGINT
# Start a demo job in the background
for i in 1 2 3 4 5; do date; sleep 1; done &
job_pid=$!
# Call wait in a loop; wait will return 0 if the job exits, and 128+$signum if interrupted by a signal.
while ! wait; do
echo "resuming wait"
done
job_pid=
echo last exit code: $?
Existem limitações para essa abordagem no shell:
- Há uma condição de corrida: se você pressionar Ctrl + C logo após a conclusão da tarefa, mas antes da linha
kill
, o manipulador de sinal tentará matarjob_pid=
, mas o processo não existe mais (mesmo como um zumbi, porque$jobpid
já o colheu) e o ID do processo pode ter sido reutilizado por outro processo. Isso não é facilmente corrigível no shell (talvez configurando um manipulador parawait
?). - Se você precisar do status de retorno do trabalho, precisará usar o formulário
SIGCHLD
. Mas então você não pode distinguir "wait $job_pid
foi interrompido por um sinal" de "o trabalho foi morto por um sinal" (nem de "o trabalho terminou por sua própria vontade com um status de retorno ≥128", mas isso é um geral fato na programação shell). - Isso não se estenderá facilmente a todos os sub-trabalhos. Note que o comportamento de armadilhas e sinais é geralmente surpreendente quando você vai além do básico na maioria das implementações de shell (somente o ksh faz isso bem).
Para superar essas limitações, use uma linguagem mais sofisticada, como Perl ou Python.