Eu tenho um script bash que inicia um processo filho que trava (na verdade trava) de tempos em tempos e sem motivo aparente (código fechado, para que não haja muito o que fazer). Como resultado, eu gostaria de poder iniciar esse processo por um determinado período de tempo e eliminá-lo se não retornar com êxito após um determinado período de tempo.
Tem alguma simples e robusta de conseguir isso usando o bash?
PS: diga-me se esta pergunta é mais adequada para falha do servidor ou superusuário.
Respostas:
(Como visto em: Entrada no BASH FAQ # 68: "Como executo um comando e o aborto (timeout) após N segundos?" )
Se você não se importa de baixar algo, use
timeout
(sudo apt-get install timeout
) e use-o como: (a maioria dos sistemas já o instalou, caso contrário, usesudo apt-get install coreutils
)Se você não deseja fazer o download de algo, faça o tempo limite internamente:
Caso deseje fazer um tempo limite para um código bash mais longo, use a segunda opção da seguinte maneira:
fonte
cmdpid=$BASHPID
não aceitará o pid do shell de chamada , mas o (primeiro) subshell iniciado por ele()
. O(sleep
... coisa chama um segundo subnível no primeiro subnível que esperar 10 segundos em segundo plano e matar o primeiro subnível que, depois de ter lançado o processo de subnível assassino, passa a executar a sua carga de trabalho ...timeout
faz parte do GNU coreutils, portanto já deve estar instalado em todos os sistemas GNU.timeout
agora faz parte dos coreutils.ou para obter os códigos de saída também:
fonte
kill -9
antes de tentar sinais de que um processo pode processar primeiro.dosmth
terminar em 2 segundos, outro processo recebe o antigo pid e você mata o novo?fonte
sleep 999
aqui) frequentemente terminar mais rápido que o sono imposto (sleep 10
)? E se eu quiser dar uma chance de até 1 minuto, 5 minutos? E se eu tiver um monte de tais casos no meu script :)Eu também tive essa pergunta e achei mais duas coisas muito úteis:
Então, eu uso algo assim na linha de comando (OSX 10.9):
Como este é um loop, incluí um "sleep 0.2" para manter a CPU fresca. ;-)
(BTW: ping é um mau exemplo de qualquer maneira, você apenas usaria a opção "-t" (timeout) incorporada.)
fonte
Supondo que você tenha (ou possa criar facilmente) um arquivo pid para rastrear o pid da criança, você poderá criar um script que verifique o horário do arquivo pid e interrompa / repita o processo conforme necessário. Em seguida, basta colocar o script no crontab para executar aproximadamente no período necessário.
Deixe-me saber se você precisar de mais detalhes. Se isso não parece adequado às suas necessidades, e o iniciante?
fonte
Uma maneira é executar o programa em um subshell e se comunicar com o subshell através de um pipe nomeado com o
read
comando Dessa forma, você pode verificar o status de saída do processo em execução e comunicá-lo de volta através do canal.Aqui está um exemplo de tempo limite do
yes
comando após 3 segundos. Ele obtém o PID do processo usandopgrep
(possivelmente só funciona no Linux). Há também algum problema com o uso de um pipe, pois um processo de abertura de um pipe para leitura será interrompido até que ele também seja aberto para gravação e vice-versa. Portanto, para evitar que oread
comando seja interrompido, "abri" o pipe para leitura com um subshell em segundo plano. (Outra maneira de impedir que um congelamento abra o pipe de leitura e gravação, ou sejaread -t 5 <>finished.pipe
, isso também pode não funcionar, exceto no Linux.)fonte
Aqui está uma tentativa que tenta evitar matar um processo depois que ele já saiu, o que reduz a chance de matar outro processo com o mesmo ID de processo (embora seja provavelmente impossível evitar completamente esse tipo de erro).
Use like
run_with_timeout 3 sleep 10000
, que rodasleep 10000
mas termina após 3 segundos.Isso é como outras respostas que usam um processo de tempo limite em segundo plano para interromper o processo filho após um atraso. Eu acho que isso é quase o mesmo que a resposta estendida de Dan ( https://stackoverflow.com/a/5161274/1351983 ), exceto que o shell de tempo limite não será eliminado se já tiver terminado.
Após a conclusão desse programa, ainda haverá alguns processos de "suspensão" remanescentes em execução, mas eles devem ser inofensivos.
Essa pode ser uma solução melhor do que minha outra resposta, pois não usa o recurso de shell não portátil
read -t
e nãopgrep
.fonte
(exec sh -c "$*") &
esh -c "$*" &
? Especificamente, por que usar o primeiro em vez do último?Aqui está a terceira resposta que enviei aqui. Este lida com interrupções de sinal e limpa processos em segundo plano quando
SIGINT
é recebido. Ele usa o truque$BASHPID
eexec
usado na resposta superior para obter o PID de um processo (neste caso,$$
em umash
invocação). Ele usa um FIFO para se comunicar com um subshell que é responsável por matar e limpar. (Isso é como o pipe na minha segunda resposta , mas ter um pipe nomeado significa que o manipulador de sinais também pode escrever nele.)Eu tentei evitar as condições da corrida o máximo possível. No entanto, uma fonte de erro que não pude remover é quando o processo termina quase ao mesmo tempo que o tempo limite. Por exemplo,
run_with_timeout 2 sleep 2
ourun_with_timeout 0 sleep 0
. Para mim, este último dá um erro:como ele está tentando matar um processo que já saiu por si só.
fonte