Teclas de sinal como Ctrl + C enviam um sinal para todos os processos em primeiro plano grupo de processos .
No caso típico, um grupo de processos é um pipeline. Por exemplo, em head <somefile | sort
, o processo executando head
e o processo executando sort
estão no mesmo grupo de processo, assim como o shell, então todos recebem o sinal. Quando você executa um trabalho em segundo plano ( somecommand &
), esse trabalho está em seu próprio grupo de processos, portanto, pressionar Ctrl + C não o afeta.
O programa timeout
se coloca em seu próprio grupo de processos. Do código-fonte:
/* Ensure we're in our own group so all subprocesses can be killed.
Note we don't just put the child in a separate group as
then we would need to worry about foreground and background groups
and propagating signals between them. */
setpgid (0, 0);
Quando ocorre um tempo limite, timeout
passa pelo expediente simples de eliminar o grupo de processos do qual é membro. Como ele se colocou em um grupo de processos separado, seu processo pai não estará no grupo. Usar um grupo de processos aqui garante que, se o aplicativo filho forfegar em vários processos, todos os seus processos receberão o sinal.
Quando você executa timeout
diretamente na linha de comando e pressiona Ctrl + C , o SIGINT resultante é recebido por timeout
e pelo processo filho, mas não pelo shell interativo que é o processo pai de timeout
. Quando timeout
é chamado de um script, apenas o shell que está executando o script recebe o sinal: timeout
não o obtém, pois está em um grupo de processos diferente.
Você pode definir um manipulador de sinal em um script de shell com o trap
builtin. Infelizmente, não é tão simples assim. Considere isto:
#!/bin/sh
trap 'echo Interrupted at $(date)' INT
date
timeout 5 sleep 10
date
Se você pressionar Ctrl + C após 2 segundos, isto ainda espera os 5 segundos completos, depois imprime a mensagem “Interrompido”. Isso ocorre porque o shell evita executar o código de interceptação enquanto um trabalho em primeiro plano está ativo.
Para corrigir isso, execute o trabalho em segundo plano. No manipulador de sinal, chame kill
para retransmitir o sinal para o grupo de processos timeout
.
#!/bin/sh
trap 'kill -INT -$pid' INT
timeout 5 sleep 10 &
pid=$!
wait $pid