Terminando um loop infinito

52

Eu tenho um comando que eu quero executar novamente automaticamente sempre que termina, então executei algo como isto:

while [ 1 ]; do COMMAND; done;

mas se eu não conseguir parar o loop Ctrl-c, isso simplesmente mata COMMANDe não o loop inteiro.

Como eu conseguiria algo semelhante, mas que eu posso parar sem precisar fechar o terminal?

howardh
fonte
11
Se estou no bash, basta usar Ctrl-Z para interromper o trabalho e depois "matar% 1" para matá-lo.
Paul Cager 04/04/12
3
Apenas espere ... Linus foi citado como tendo dito: “Todos sabemos que o Linux é ótimo ... ele faz loops infinitos em 5 segundos.” - realmente ... apenas espere mais alguns segundos, ele deve ser concluído.
Lornix
@PaulCager também trabalhou para mim! Por que funciona onde Ctrl-C não funciona?
Ciro Santilli # 15/14
@cirosantilli mata o trabalho externo (o "wrapper" do bash). Em algumas situações, ele não mata imediatamente o "COMANDO", por exemplo, se você o coloca em segundo plano, ele pode passar vivo, mesmo que seu pai esteja morto. Mas o loop está morto, e essa é a parte importante.
precisa

Respostas:

38

Verifique o status de saída do comando. Se o comando foi finalizado por um sinal, o código de saída será 128 + o número do sinal. Na documentação on-line do GNU para bash :

Para os propósitos do shell, um comando que sai com um status de saída zero foi bem-sucedido. Um status de saída diferente de zero indica falha. Esse esquema aparentemente contra-intuitivo é usado para que haja uma maneira bem definida de indicar sucesso e várias maneiras de indicar vários modos de falha. Quando um comando termina em um sinal fatal cujo número é N, Bash usa o valor 128 + N como status de saída.

O POSIX também especifica que o valor de um comando finalizado por um sinal é maior que 128, mas não parece especificar seu valor exato como o GNU:

O status de saída de um comando que terminou porque recebeu um sinal deve ser relatado como superior a 128.

Por exemplo, se você interromper um comando com o controle-C, o código de saída será 130, porque SIGINT é o sinal 2 nos sistemas Unix. Assim:

while [ 1 ]; do COMMAND; test $? -gt 128 && break; done
Kyle Jones
fonte
9
Deve-se mencionar que isso não é garantido; de fato, muitos aplicativos não farão isso.
Chris Baixo
11
@ Kyle Jones: você pode criar um link para os documentos POSIX / GNU que mencionam isso?
Ciro Santilli escreveu
@cirosantilli Concluído.
Kyle Jones
@KyleJones thanks! Ainda na prática não está trabalhando para COMMAND = paplay alert.ogg, talvez porque paplaylida com o sinal?
Ciro Santilli # 16/14
@cirosantilli Sim, essa é a razão. Se um processo manipula o sinal e sai, isso é diferente do processo sendo finalizado por um sinal não tratado.
Kyle Jones
49

Você pode parar e colocar seu trabalho em segundo plano enquanto ele estiver sendo executado usando ctrl+ z. Então você pode matar seu trabalho com:

$ kill %1

Onde [1] é o seu número de trabalho.

Jem
fonte
Veja também esta resposta para explicações e muito mais.
Skippy le Grand Gourou
2
Essa resposta relativamente recente simplesmente funciona. Precisa ser votado. +1
shivams
Você me ajudou muito. Isto é o que eu tenho procurado nesta questão :)
Maximilian Ruta
18

Eu diria que talvez seja melhor colocar seu loop infinito em um script e manipular sinais lá. Aqui está um ponto de partida básico . Tenho certeza que você deseja modificá-lo para se adequar. O script usa trappara pegar ctrl- c(ou SIGTERM), mata o comando (eu usei sleepaqui como teste) e sai.

cleanup ()
{
kill -s SIGTERM $!
exit 0
}

trap cleanup SIGINT SIGTERM

while [ 1 ]
do
    sleep 60 &
    wait $!
done
ephsmith
fonte
4
Agradável. Aqui está como eu usei essa dica para criar um invólucro de netcat com início automático:trap "exit 0" SIGINT SIGTERM; while true; do netcat -l -p 3000; done
Douglas
2
se você adicionar esta trapabordagem para o mesmo script (bash) com o loop infinito para ser morto, o uso $$em vez de $!(ver aqui )
ardnew
15

Eu geralmente apenas me seguro Ctrl-C. Mais cedo ou mais tarde, ele se registrará entre COMMAND's e, assim, encerrará o whileloop. Talvez haja uma maneira melhor.

jw013
fonte
11
Não sei por que, mas falha em determinados COMMANDs, como paplayem um arquivo 1s.
Ciro Santilli escreveu
Funcionou para mim #
2828 alexandr
Essa é a força bruta de todas as soluções aqui. : /
markroxor 6/01
8

Se você executar o bash, -eele sairá em qualquer condição de erro:

#!/bin/bash -e
false # returns 1
echo This won't be printed
Restabelecer Monica
fonte
8

Por que não simplesmente,

while [ 1 ]; do COMMAND || break; done;

Ou quando usado em um script,

#!/bin/bash
while [ 1 ]; do
  # ctrl+c terminates COMMAND and exits the while loop
  # (assuming COMMAND responds to ctrl+c)
  COMMAND || break
done;
Dale Anderson
fonte
11
Solução muito elegante. Mas isso não funcionaria apenas se COMMAND sempre retornasse um status de saída com sucesso?
howardh
Sim @ howardh, está correto.
Dale Anderson
3

Eu prefiro outra solução:

touch .runcmd; while [ -f ".runcmd" ]; do COMMAND; sleep 1; done

Para eliminar o loop, basta:

rm .runcmd && kill `pidof COMMAND`
M4tze
fonte
2
  1. Você sempre pode matar um processo usando seu PID, não há necessidade de fechar seu terminal
  2. Se você deseja executar algo em um loop infinito como um daemon, é melhor colocá-lo em segundo plano
  3. while : criará um loop infinito e poupará você a escrever o [ 1 ]

    while :; do COMMAND; done &

Isso imprimirá o PID. Se você sair do prompt usando ctrl+d, o trabalho em segundo plano não será encerrado e, posteriormente, você poderá matá-lo de qualquer lugar usandokill PID

Se você perder o controle do seu PID, poderá usar pstree -pa $USERou pgrep -fl '.*PROCESS.*'para ajudá-lo a encontrá-lo

errant.info
fonte
0

Use trap-

exit_()
{
    exit
}

while true
do
    echo 'running..'
    trap exit_ int
done
markroxor
fonte