confirmou saída usando armadilha

9

Estou tentando interceptar o sinal Ctrl+C solicitando uma confirmação do usuário. A parte de trapping funciona bem. Mas uma vez que o sinal fica preso, ele não retorna à execução normal. Em vez disso, ele sai do script. Como fazê-lo retomar a execução quando o usuário pressiona não.

aqui está o meu código

hell()
{
echo "Do you want to quit? Press 1 for yes and 0 for no";
read n;
if [ $n == 1 ]; then
exit 1;
fi
}

trap "hell" SIGINT

find /
    
por CHID 28.06.2011 / 08:12

1 resposta

11

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á matar job_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 para wait ?).
  • 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.

    
por 28.06.2011 / 14:09