O que está acontecendo
Quando você pressiona Ctrl+ C, o SIGINT
sinal é entregue a todo o grupo de processos em primeiro plano . Aqui é enviado para o find
processo e o processo de shell de chamada. find
reage saindo imediatamente, e a concha reage chamando a armadilha.
Se o código na armadilha retornar (ou seja, não chamar exit
), a execução continuará com o comando após o que foi interrompido pelo sinal. Aqui, após o find
comando chegar o final do script, o script sai imediatamente de qualquer maneira. 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)
Você pode fazer o que você quiser; mas essa parte da minha resposta é mais sobre descobrir a programação de shell do que resolver um problema real.
- Por 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+ Cmate o script.
- Como você verá abaixo, ele estende os recursos do shell um pouco longe de qualquer maneira.
Se você quiser evitar o abate find
, é necessário iniciá-lo no fundo : find / &
. Em seguida, use o wait
built-in para esperar que ele saia normalmente. Um sinal interromperá o wait
built-in, que você pode executar em loop até receber o sinal que deseja propagar. Então use kill
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:
- Existe uma condição de corrida: se você pressionar Ctrl+ Clogo após o trabalho terminar, mas antes da
job_pid=
linha, o manipulador de sinais tentará matar $jobpid
, mas o processo não existe mais (mesmo como um zumbi, porque wait
já o colheu) e o processo O ID pode ter sido reutilizado por outro processo. Isso não é facilmente corrigível no shell (talvez configurando um manipulador para SIGCHLD
?).
- Se você precisar do status de retorno do trabalho, precisará usar o
wait $job_pid
formulário. Mas você não pode distinguir “ wait
foi interrompido por um sinal” de “o trabalho foi morto por um sinal” (nem de “o trabalho foi finalizado por conta própria com um status de retorno ≥128”, mas isso é um fato geral em geral programação).
- Isso não se estenderá facilmente se houver vários submobs. Observe que o comportamento de traps e sinais geralmente é surpreendente quando você vai além do básico na maioria das implementações de shell (apenas o ksh faz isso bem).
Para superar essas limitações, use uma linguagem mais sofisticada, como Perl ou Python.