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:
- Envio e sinalização de armadilhas
- Impedindo a propagação de SIGINT para o processo pai
- O TTY desmistificado
Sugiro:
- Enviando o
tail
para o plano de fundo - Trape o sinal
- Mate o processo em segundo plano.
- 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.