timeout sem matar o processo no bash

4

Eu tenho um script principal em execução e, a partir dele, tenho um segundo "processo lento" que quero iniciar e "fazer alguma coisa" no script principal, se ele não for concluído no limite de tempo - dependendo se completou ou não. N.B. Se o "processo lento" terminar antes do meu limite de tempo, não quero ter que esperar um limite de tempo inteiro.

Eu quero que o "processo lento" continue, para que eu possa reunir estatísticas e análises forenses sobre seu desempenho.

Eu olhei para o tempo limite , mas ele irá matar meu script quando terminar.

Suponha este exemplo simplificado.

main.sh

result='timeout 3 ./slowprocess.sh'
if [ "$result" = "Complete" ]
then
 echo "Cool it completed, do stuff..."
else
 echo "It didn't complete, do something else..."
fi

slowprocess.sh

#!/bin/bash
start='date +%s'
sleep 5
end='date +%s'
total='expr $end - $start'
echo $total >> /tmp/performance.log
echo "Complete"

Aqui, ele usa timeout - então o script morre, então nada acaba em /tmp/performance.log - eu quero que slowprocess.sh seja concluído, mas, eu quero que main.sh vá para a próxima etapa mesmo que não terminar nos 3 segundos.

    
por dougBTV 30.07.2013 / 22:58

3 respostas

7

Com bash:

{
  (./slowprocess.sh >&3 3>&-; echo "$?") |
    if TMOUT=3 read status; then
      echo "Cool it completed with status $status, do stuff..."
    else
      echo "It didn't complete, do something else..."
    fi
} 3>&1

Alternativamente, se você quiser usar timeout (aqui assumindo o GNU timeout ):

timeout --foreground 3 sh -c './slowprocess.sh;:'

evitaria que slowprocess.sh fosse eliminado (o ;: é necessário para implementações sh que otimizam executando o último comando no processo do shell).

    
por 30.07.2013 / 23:46
2

Aqui está uma solução usando apenas ferramentas de shell onipresentes.

Isso deve ser feito facilmente, bifurcando o processo lento e um sleep em segundo plano e aguardando o primeiro terminar, exceto que o wait shell interno aguarda que todos os trabalhos sejam concluídos em vez de apenas para o primeiro.

Então, em vez disso, bifurque o processo lento e um sleep no plano de fundo, faça com que os dois relatem seu status por meio de um canal e leia o primeiro status que sai do canal.

fifo=$(mktemp -u)  # if not on Linux, adapt to what your OS provides
mkfifo -m 600 "$fifo"
{ ./slowprocess.sh; echo z >"$fifo"; } &
sh -c 'sleep 3; echo a' >"$fifo" &
sleep_pgid=$!
read status <$fifo
case $status in
  a) echo "That process is taking a long time"; read ignored <$fifo;;
  z) echo "Done already"; kill -INT -$sleep_pgid;;
esac
rm "$fifo"
    
por 31.07.2013 / 02:56
0

Tudo o que está bloqueando o slowprocess.sh pode ser feito em segundo plano para que possa continuar mesmo após o tempo limite.

Você também pode usar um signal de timeout de volta para seu main.sh . Veja a man page para timeout .

trecho da página de informações timeout

'-s SIGNAL'
'--signal=SIGNAL'
     Send this SIGNAL to COMMAND on timeout, rather than the default
     'TERM' signal. SIGNAL may be a name like 'HUP' or a number. Also
     see *Note Signal specifications::.

Você teria que capturar esse sinal dentro de main.sh .

Além disso, timeout retorna status diferentes para diferentes tipos de falhas. Você também pode fazer uso deles:

   Exit status:

     124 if COMMAND times out
     125 if 'timeout' itself fails
     126 if COMMAND is found but cannot be invoked
     127 if COMMAND cannot be found
     the exit status of COMMAND otherwise

Estes estão presentes na variável $? , após a execução da linha timeout ... .

    
por 30.07.2013 / 23:19