Evita que o SIGINT interrompa a chamada de função e o (s) processo (s) filho (s) dentro de

2

Considere o seguinte script:

#!/bin/bash

set -o pipefail
set -o history

trapper() {
   func="$1" ; shift
   for sig ; do
      trap "$func $sig" "$sig"
   done
}

err_handler () {
  case $2 in
    INT)
       stop_received=1
    ;;
    TSTP)
    ;;
    ERR)
       if [[ $2 != "INT" ]]; then # for some reason, ERR gets triggered on SIGINT
          code=$?
          if [ $code -ne 0 ]; then
             echo "Failed on line $1"
             echo "$BASH_COMMAND returned $?"
             echo "Content of variables at the time of failure:"
             echo "$(set -o posix; set)"
             exit 1
          fi
       fi
    ;;
  esac
}

main() {
   ping -c 5 www.google.com # this is a test to see if INT interrupts main()
   # do a bunch of stuff, I mean a real bunch of stuff, like check some
   # files, do some processing, connect to a database,
   # do more processing, move some files around, you get the drift
}

exec > >(tee -a my.log)
exec 2>&1

trapper 'err_handler $LINENO' INT TSTP ERR
while main
do
  if [[ "$stop_received" == "1" ]]; then
     break
  fi
  setsid sleep 2 & wait
done
trap ERR

O que eu estou tentando realizar é executar o script em um loop infinito até que a função main () retorne algum valor diferente de zero, ou seja, algum erro ocorreu ou SIGINT é recebido.

No entanto, eu não quero que o SIGINT pare a execução do main (), em outras palavras, se o script receber um SIGINT, ele deve esperar que o main () termine, e saia bem. Mas quando eu clico em CTRL + C, vejo que o ping é interrompido. No momento eu comentei tudo sob main () só para ver se isso funciona. Como o ping é interrompido, estou assumindo que outros comandos em main () também seriam interrompidos. Quando o ping é interrompido, o processamento salta para a linha onde eu verifico se $ stop_received = 1 e, em seguida, o loop é interrompido e o script é encerrado. Se eu substituir a quebra apenas por um eco, o script continuará para a próxima iteração do loop principal while.

Como posso impedir que o SIGINT interrompa o (s) comando (s) atualmente em execução? Como meu script faz um monte de coisas, incluindo instruções DML em um banco de dados, interromper main () causaria muito sofrimento.

Em segundo lugar, o script não captura ctrl + z também. Ou melhor, o script só fica preso em ctrl + z, exigindo um kill pid para terminar. Eu assumi que o sono sendo um filho do bash em oposição ao script em si, ctrl + z faria com que o script parasse, deixando o sono no limbo. Daí o setsid e esperar no sono, mas ainda trava.

obrigado.

    
por Jim Walker 21.09.2014 / 19:58

1 resposta

2

Existem várias maneiras de cortar o efeito de Ctrl + C :

  • Altere a configuração do terminal para que não gere um sinal.
  • Bloqueie o sinal para que seja salvo para entrega posterior, quando o sinal for desbloqueado.
  • Ignore o sinal ou defina um manipulador para ele.
  • Executar subprocessos em um grupo de processos em segundo plano.

Como você deseja detectar que Ctrl + C foi pressionado, ignorar o sinal está fora. Você pode alterar as configurações do terminal, mas precisará escrever código de processamento de chave personalizado. As conchas não fornecem acesso ao bloqueio de sinal.

No entanto, você pode isolar subprocessos de receber o sinal automaticamente, executando-os em um grupo de processos separado. Shells interativos executam comandos de segundo plano em um grupo de processos separado por padrão, mas shells não interativos os executam no mesmo grupo de processos, e todos os processos no grupo de processos de primeiro plano recebem um sinal de eventos de terminal. Para dizer ao shell para executar tarefas em segundo plano em um grupo de processos separado, execute set -m . A execução de setsid ping … é outra maneira de forçar o ping a ser executado em um grupo de processos separado.

set -m
interrupted=
trap 'echo Interrupted, but ping may still be running' INT
set -m
ping … &
while wait; [ $? -ge 128 ]; do echo "Waiting for background jobs"; done
echo ping has finished

Se você quiser Ctrl + Z para suspender um grupo de processos em segundo plano, você precisará propagar o sinal do shell.

Controlar sinais com precisão é um pouco difícil para um shell script, e shells diferentes de ATT ksh tendem a ser um pouco problemáticos quando você alcança os casos de canto, então considere uma linguagem que lhe dê mais controle como Perl, Python. ou Ruby.

    
por 22.09.2014 / 02:46