Por que pressionar Ctrl + C quando em um aplicativo foi iniciado a partir de um script, quebre o script?

3

Para pular direto:

while true; do
    #---  MENU LOGIC HERE, stick response in $MENUEXIT

    #----Deal with responses here

    if [ $S1 == $MENUEXIT ];
    then
            tail -f /path-to-file
    else
            sleep 2
    fi
done

Eu tentei manter isso o mais simples possível, se você precisar de mais, me avise.

Basicamente, estou colocando um menu em um loop, então, se houver entrada incorreta do usuário ou um comando terminar, ele será exibido para exibir novamente o menu.

Funcionou bem até eu adicionar o comando tail.

Se eu escolher a opção tail do menu, o comando tail será lançado bem - mas, se eu clicar Ctrl + C , eu gostaria que a cauda terminasse e o menu a ser exibido, mas em vez disso termina o tail AND o script.

Eu tentei várias coisas, como continua / armadilhas e mais, mas, eu bati uma parede de tijolos e gostaria de alguma ajuda por favor?!

    
por wilhil 09.08.2014 / 03:33

1 resposta

7

Quando você envia um sinal como SIGINT ( Ctrl C ) ou SIGSTOP ( Ctrl Z ) de um terminal , o sinal é enviado para o grupo de processos em primeiro plano. Ou seja, o grupo que compreende o job em primeiro plano (seu script) e qualquer processo filho que ele tenha em primeiro plano (o comando tail ). Isso faz com que todos esses processos saiam (ou façam o que for que eles fazem ao manipular o sinal). Você pode testar a diferença enviando um sinal usando o comando kill :

Em um terminal, execute este script (estou chamando test nos seguintes comandos):

#! /bin/bash

while true
do
    if [[  = a ]]
    then
        yes 
    else
        echo no
        sleep 2
    fi
done

Em outro, execute estes comandos:

pstree -ps $(pgrep -f test)
pkill -f test
pstree -ps $(pgrep -f yes)

A saída dos comandos seria algo assim:

init(1)───lightdm(1032)───lightdm(1154)───init(1173)───x-terminal-emul(17009)───bash(24262)───test.sh(24996)───yes(24997)
# No output for pkill
init(1)───lightdm(1032)───lightdm(1154)───init(1173)───yes(24997)

(Os números e programas reais podem variar, mas o primeiro init sempre terá pid 1 .)
Como você pode ver, o comando yes não foi morto e foi anexado a init porque seu pai foi morto. Ainda está imprimindo alegremente y s no primeiro terminal. Então mate-o com pkill -f yes . Agora repita o experimento, com uma mudança. Em vez de pkill -f test , faça:

kill -INT -25165 
# Use the pid of test.sh given in parentheses in the output of pstree 
# instead of 25165

Observe o - inicial. No Linux, para o comando kill , -25165 é o grupo de processos, cujo líder tem pid 25165 . Assim, este comando é o equivalente a enviar uma interrupção do terminal.

Naturalmente, o comportamento exato depende da configuração do TTY, do shell de login e assim por diante. Isso é tanto quanto o meu entendimento vai. Leitura adicional:

Sugiro:

  1. Enviando o tail para o plano de fundo
  2. Trape o sinal
  3. Mate o processo em segundo plano.
  4. Remova a armadilha

Um exemplo:

#! /bin/bash

kill_jobs ()
{
    kill %1
}

while true
do
    if [[  = "a" ]]
    then
        trap kill_jobs INT
        tail -f /var/log/syslog &
        wait %%
        shift #You don't need to shift, I just didn't want loop tail on this example
        trap - INT
    else
        echo some output
        sleep 2
    fi
done

Isso pode se tornar muito poderoso, usando diferentes funções para trapping, e cada um fazendo seu próprio trabalho de limpeza.

    
por muru 09.08.2014 / 03:57

Tags