Por que não posso matar um tempo limite chamado de um script Bash com um pressionamento de tecla?

9

[Edit: Isto é semelhante a algumas outras questões perguntando como matar todos os processos gerados - as respostas parecem ser o uso de pkill. Então, o núcleo da minha pergunta pode ser: Existe uma maneira de propagar Ctrl-C / Z para todos os processos gerados por um script?]

Ao chamar um SoX rec com o comando timeout do coreutils (discutido aqui ), não há Parece ser uma maneira de matá-lo com um pressionamento de tecla, uma vez que foi invocado a partir de um script Bash.

Exemplos:

timeout 10 rec test.wav

... pode ser eliminado com Ctrl + C ou Ctrl + Z do bash, mas não quando é chamado de dentro de um script.

timeout 10 ping nowhere

... pode ser eliminado com Ctrl + C ou Ctrl + Z do bash, e com Ctrl + Z quando é executado dentro de um script.

Eu posso encontrar o ID do processo e eliminá-lo dessa maneira, mas por que não posso usar um pressionamento de tecla de quebra padrão? E existe alguma maneira de estruturar meu script para que eu possa?

    
por meetar 05.12.2012 / 21:11

3 respostas

14

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
    
por 06.12.2012 / 02:01
8

Com base na excelente resposta fornecida por Gilles. O comando timeout tem uma opção de primeiro plano, se usado, então um CTRL + C sairá do comando timeout.

#!/bin/sh
trap 'echo caught interrupt and exiting;exit' INT
date
timeout --foreground 5 sleep 10
date
    
por 03.10.2015 / 15:13
1

Eu acho que você não entende o que está acontecendo aqui.

Basicamente Ctrl + C envia um sinal SIGINT, enquanto Ctrl + Z envia um sinal SIGTSTP.

O SIGTSTP apenas interrompe o processo - um SIGCONT continuará.

Isso funciona no processo forground que foi bifurcado na linha de comando.

Se o seu processo for um processo em segundo plano, você terá que enviar esse sinal para os processos de uma maneira diferente. kill fará isso. Em teoria, um operador "-" nesse kill também deve sinalizar processos filhos, mas isso raramente funciona como esperado.

Para ler mais: Sinais do Unix

    
por 05.12.2012 / 22:10