Tempo limite de um comando no bash sem atraso desnecessário


283

Esta resposta ao comando da linha de comandos para matar automaticamente um comando após um certo período de tempo

propõe um método de 1 linha para atingir o tempo limite de um comando de longa execução a partir da linha de comando do bash:

( /path/to/slow command with options ) & sleep 5 ; kill $!

Mas é possível que um determinado comando "de longa execução" termine antes do tempo limite. (Vamos chamá-lo de um comando "normalmente de longa execução, mas às vezes rápido", ou tlrbsf por diversão.)

Portanto, essa abordagem bacana de uma linha apresenta alguns problemas. Primeiro, sleepnão é condicional, de modo que define um limite inferior indesejável para o tempo necessário para a sequência terminar. Considere 30s ou 2m ou até 5m para o sono, quando o comando tlrbsf terminar em 2 segundos - altamente indesejável. Segundo, como killé incondicional, portanto, essa sequência tentará eliminar um processo que não está em execução e se queixar.

Assim...

Existe uma maneira de atingir o tempo limite de um comando tipicamente de execução longa, mas às vezes rápido ( "tlrbsf" ) que

  • possui uma implementação bash (a outra pergunta já possui respostas Perl e C)
  • terminará no início dos dois: término do programa tlrbsf ou tempo limite decorrido
  • não matará processos inexistentes / não em execução (ou, opcionalmente: não reclamará de uma matança incorreta)
  • não precisa ser um liner
  • pode ser executado no Cygwin ou Linux

... e, para pontos de bônus, executa o comando tlrbsf em primeiro plano e qualquer processo 'sleep' ou extra em segundo plano, de modo que o stdin / stdout / stderr do comando tlrbsf possa ser redirecionado, mesmo como se tivesse sido correr diretamente?

Nesse caso, compartilhe seu código. Se não, por favor explique o porquê.

Passei um tempo tentando hackear o exemplo mencionado, mas estou atingindo o limite de minhas habilidades no bash.


5
Outra pergunta semelhante: stackoverflow.com/questions/526782/… (mas acho que a resposta 'timeout3' aqui é muito melhor).
system PAUSE

2
Algum motivo para não usar o timeoututilitário gnu ?
31418 Chris Johnson

timeouté ótimo! você pode até usar com vários comandos (script de várias linhas): stackoverflow.com/a/61888916/658497
Noam Manos

Respostas:


149

Eu acho que é exatamente isso que você está pedindo:

http://www.bashcookbook.com/bashinfo/source/bash-4.0/examples/scripts/timeout3

#!/bin/bash
#
# The Bash shell script executes a command with a time-out.
# Upon time-out expiration SIGTERM (15) is sent to the process. If the signal
# is blocked, then the subsequent SIGKILL (9) terminates it.
#
# Based on the Bash documentation example.

# Hello Chet,
# please find attached a "little easier"  :-)  to comprehend
# time-out example.  If you find it suitable, feel free to include
# anywhere: the very same logic as in the original examples/scripts, a
# little more transparent implementation to my taste.
#
# Dmitry V Golovashkin <Dmitry.Golovashkin@sas.com>

scriptName="${0##*/}"

declare -i DEFAULT_TIMEOUT=9
declare -i DEFAULT_INTERVAL=1
declare -i DEFAULT_DELAY=1

# Timeout.
declare -i timeout=DEFAULT_TIMEOUT
# Interval between checks if the process is still alive.
declare -i interval=DEFAULT_INTERVAL
# Delay between posting the SIGTERM signal and destroying the process by SIGKILL.
declare -i delay=DEFAULT_DELAY

function printUsage() {
    cat <<EOF

Synopsis
    $scriptName [-t timeout] [-i interval] [-d delay] command
    Execute a command with a time-out.
    Upon time-out expiration SIGTERM (15) is sent to the process. If SIGTERM
    signal is blocked, then the subsequent SIGKILL (9) terminates it.

    -t timeout
        Number of seconds to wait for command completion.
        Default value: $DEFAULT_TIMEOUT seconds.

    -i interval
        Interval between checks if the process is still alive.
        Positive integer, default value: $DEFAULT_INTERVAL seconds.

    -d delay
        Delay between posting the SIGTERM signal and destroying the
        process by SIGKILL. Default value: $DEFAULT_DELAY seconds.

As of today, Bash does not support floating point arithmetic (sleep does),
therefore all delay/time values must be integers.
EOF
}

# Options.
while getopts ":t:i:d:" option; do
    case "$option" in
        t) timeout=$OPTARG ;;
        i) interval=$OPTARG ;;
        d) delay=$OPTARG ;;
        *) printUsage; exit 1 ;;
    esac
done
shift $((OPTIND - 1))

# $# should be at least 1 (the command to execute), however it may be strictly
# greater than 1 if the command itself has options.
if (($# == 0 || interval <= 0)); then
    printUsage
    exit 1
fi

# kill -0 pid   Exit code indicates if a signal may be sent to $pid process.
(
    ((t = timeout))

    while ((t > 0)); do
        sleep $interval
        kill -0 $$ || exit 0
        ((t -= interval))
    done

    # Be nice, post SIGTERM first.
    # The 'exit 0' below will be executed if any preceeding command fails.
    kill -s SIGTERM $$ && kill -0 $$ || exit 0
    sleep $delay
    kill -s SIGKILL $$
) 2> /dev/null &

exec "$@"

Esse é um truque, usar $$ para a parte de fundo. E bom que ele mate um tlrbsf pendurado imediatamente. Mas ugh, você precisa escolher um intervalo de pesquisa. E se você definir a pesquisa muito baixa, ela consumirá a CPU com sinalização constante, tornando o tlrbsf ainda mais longo!
system PAUSE

7
Você não precisa escolher o intervalo de pesquisa, o padrão é 1s, o que é muito bom. E a verificação é muito barata, a sobrecarga é insignificante. Duvido que isso tornaria o tlrbsf visivelmente mais longo. Eu testei com o sono 30 e obtive 0,000ms de diferença entre usá-lo e não usá-lo.
Juliano

6
Certo, eu vejo isso agora. E ele atende aos meus requisitos exatos se você definir o intervalo da pesquisa == timeout. Também trabalha em pipelines, trabalha com a coisa toda em segundo plano, trabalha com várias instâncias e outros trabalhos em execução. Doce, obrigado!
system PAUSE

Enviar um sinal mata o sub-shell, então pensei que alinhar todos os comandos de matar em uma linha os preservaria. Também habilitei a saída stderr para exibir erros inesperados. stackoverflow.com/questions/687948/...
enguia ghEEz

1
@ Juliano Essa é uma ótima maneira de lidar com intervalos, muito útil. Gostaria de saber se existe uma maneira em que podemos fazer com que o script retorne exitcode 143 quando o processo é encerrado após o tempo limite? Tentei adicionar "exit 143" logo após o comando kill, mas sempre recebo o código de saída 0 no script de chamada.
Salman A. Kagzi

528

Você provavelmente está procurando o timeoutcomando no coreutils. Como faz parte dos coreutils, é tecnicamente uma solução C, mas ainda é coreutils. info timeoutpara mais detalhes. Aqui está um exemplo:

timeout 5 /path/to/slow/command with options

21
No Mac, você pode instalá-lo via Macports ou homebrew.
Ivan Z. Siu

23
Quando instalado via homebrew no OS X o comando torna-segtimeout
ethicalhack3r

5
... qual sistema operacional você usa com coreutils antes de 2003?
Keith

5
@Keith: CentOS 5.10, por exemplo :-(
Pausado até novo aviso.

9
Para esclarecer, o comando homebrew que você precisa no OSX / mac é brew install coreutilse, em seguida, você pode usá-lo gtimeout.
Oade Schneider

37

Esta solução funciona independentemente do modo do monitor do bash. Você pode usar o sinal apropriado para finalizar o seu_command

#!/bin/sh
( your_command ) & pid=$!
( sleep $TIMEOUT && kill -HUP $pid ) 2>/dev/null & watcher=$!
wait $pid 2>/dev/null && pkill -HUP -P $watcher

O observador mata seu_command após um tempo limite determinado; o script aguarda a tarefa lenta e finaliza o observador. Observe quewait não funciona com processos que são filhos de um shell diferente.

Exemplos:

  • your_command executa mais de 2 segundos e foi encerrado

your_command interrompido

( sleep 20 ) & pid=$!
( sleep 2 && kill -HUP $pid ) 2>/dev/null & watcher=$!
if wait $pid 2>/dev/null; then
    echo "your_command finished"
    pkill -HUP -P $watcher
    wait $watcher
else
    echo "your_command interrupted"
fi
  • your_command concluído antes do tempo limite (20 segundos)

your_command terminado

( sleep 2 ) & pid=$!
( sleep 20 && kill -HUP $pid ) 2>/dev/null & watcher=$!
if wait $pid 2>/dev/null; then
    echo "your_command finished"
    pkill -HUP -P $watcher
    wait $watcher
else
    echo "your_command interrupted"
fi

1
waitretorna o status de saída do processo em que espera. Portanto, se o seu comando sair dentro do tempo alocado, mas com um status de saída diferente de zero, a lógica aqui se comportará conforme expirou, ou seja, imprima your_command interrupted. Em vez disso, você pode fazer o waitsem um ife, em seguida, verificar se o $watcherpid ainda existe, se existir, você sabe que não expirou.
George Hawkins

Não consigo descobrir por que isso é usado killem um caso, mas pkillno outro. Eu precisava usar os pkilldois para fazer isso funcionar corretamente. Eu esperaria que, se você envolver um comando (), precisará usá pkill-lo para matá-lo. Mas talvez funcione de maneira diferente se houver apenas um único comando dentro do ().
Nobar

Deus te abençoe, senhor :)
Darko Miletic

22

Ai está:

timeout --signal=SIGINT 10 /path/to/slow command with options

você pode alterar o SIGINTe 10como desejar;)


3
"timeout" faz parte do pacote coreutils no (pelo menos) Redhat, Centos, Suse e Ubuntu, então você precisará instalá-lo se não o tiver.
Akom

é realmente útil !!!!!! Você sabe por que o "timeout 5 / path / to / slow / command with options" do yingted às vezes não funciona?
Decula 19/07/19

Infelizmente isso não está no coreutilspacote do FreeBSD.
Paul Bissex

18

Você pode fazer isso inteiramente com bash 4.3e acima:

_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; r=$?; kill -9 `jobs -p`; exit $r; ) }
  • Exemplo: _timeout 5 longrunning_command args
  • Exemplo: { _timeout 5 producer || echo KABOOM $?; } | consumer
  • Exemplo: producer | { _timeout 5 consumer1; consumer2; }
  • Exemplo: { while date; do sleep .3; done; } | _timeout 5 cat | less

  • Precisa do Bash 4.3 para wait -n

  • Dá 137 se o comando foi morto, senão o valor de retorno do comando.
  • Trabalhos para tubos. (Você não precisa entrar em primeiro plano aqui!)
  • Também funciona com comandos ou funções internas do shell.
  • É executado em um subshell, portanto, nenhuma exportação de variável para o shell atual, desculpe.

Se você não precisa do código de retorno, isso pode ser ainda mais simples:

_timeout() { ( set +b; sleep "$1" & "${@:2}" & wait -n; kill -9 `jobs -p`; ) }

Notas:

  • Estritamente falando, você não precisa o ;em ; ), no entanto, faz coisa mais consistente ao ; }-Case. E o set +bprovavelmente pode ser deixado de fora também, mas é melhor prevenir do que remediar.

  • Exceto por --forground(provavelmente), você pode implementar todos os timeoutsuportes de variantes . --preserve-statusé um pouco difícil, no entanto. Isso é deixado como um exercício para o leitor;)

Esta receita pode ser usada "naturalmente" na casca (tão natural quanto para flock fd):

(
set +b
sleep 20 &
{
YOUR SHELL CODE HERE
} &
wait -n
kill `jobs -p`
)

No entanto, como explicado acima, você não pode reexportar variáveis ​​de ambiente para o shell anexo dessa maneira naturalmente.

Editar:

Exemplo do mundo real: tempo limite __git_ps1, caso demore muito (para coisas como links SSHFS lentos):

eval "__orig$(declare -f __git_ps1)" && __git_ps1() { ( git() { _timeout 0.3 /usr/bin/git "$@"; }; _timeout 0.3 __orig__git_ps1 "$@"; ) }

Edit2: Bugfix. Notei que exit 137não é necessário e faz_timeout não confiável ao mesmo tempo.

Edit3: git é um , por isso precisa de um truque duplo para funcionar satisfatoriamente.

Edit4: Esqueceu um _no primeiro _timeoutexemplo de GIT do mundo real.


1
Bash 4 pedras. Isso é tudo.
system PAUSE

1
Na verdade, isso requer o Bash 4.3 ou mais recente. cc. The 'wait' builtin has a new '-n' option to wait for the next child to change status. De: tiswww.case.edu/php/chet/bash/NEWS
Ben Reser

17

Eu prefiro "timelimit", que tem um pacote pelo menos no debian.

http://devel.ringlet.net/sysutils/timelimit/

É um pouco melhor do que o "timeout" do coreutils, porque imprime alguma coisa ao interromper o processo e também envia o SIGKILL após algum tempo, por padrão.


Parece que não funciona muito bem: / $ time timelimit -T2 sleep 10 real 0m10.003s user 0m0.000s sys 0m0.000s
hithwen

3
Use -t2 não -T2. O grande -T é o tempo desde o envio do SIGTERM até o envio do SIGKILL.
maxy

1
Gostaria de acrescentar que o timelimit 1.8 não parece funcionar bem com o fork ( timelimit -t1 ./a_forking_progmata apenas um dos dois processos), mas o tempo limite funciona.
Jeremy Cochoy

Se você deseja que o tempo limite imprima algo ao interromper o processo, use o sinalizador "-v".

10

Para atingir o tempo limite slowcommandapós 1 segundo:

timeout 1 slowcommand || echo "I failed, perhaps due to time out"

Para determinar se o comando expirou ou falhou por seus próprios motivos, verifique se o código de status é 124:

# ping for 3 seconds, but timeout after only 1 second
timeout 1 ping 8.8.8.8 -w3
EXIT_STATUS=$?
if [ $EXIT_STATUS -eq 124 ]
then
echo 'Process Timed Out!'
else
echo 'Process did not timeout. Something else went wrong.'
fi
exit $EXIT_STATUS

Observe que quando o status de saída é 124, você não sabe se o tempo limite expirou devido ao seu timeoutcomando ou se o comando foi finalizado devido a alguma lógica de tempo limite interno próprio e depois retornou 124. Você pode assumir com segurança em ambos os casos , porém, que um tempo limite de algum tipo aconteceu.



8

Meio hacky, mas funciona. Não funciona se você tiver outros processos em primeiro plano (por favor me ajude a corrigir isso!)

sleep TIMEOUT & SPID=${!}; (YOUR COMMAND HERE; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}

Na verdade, acho que você pode revertê-lo, atendendo aos seus critérios de 'bônus':

(YOUR COMMAND HERE & SPID=${!}; (sleep TIMEOUT; kill ${SPID}) & CPID=${!}; fg 1; kill ${CPID}) < asdf > fdsa

(ls -ltR / cygdrive / c / windows & SPID = $ {!}; (inativa 1s; mate $ {SPID}) & CPID = $ {!}; fg 1; mate $ {CPID})> fdsa
sistema PAUSE

bash: fg: no job control
system PAUSE

@system PAUSE, defina -m, eu acho.
strager

Eu tenho controle de trabalho (defina -m) no shell de logon. Esse é o 'm' no conteúdo himBH de $ -, mas parece desaparecer para os subshells. Possivelmente um artefato Cygwin. grumble
system PAUSE

Não use "fg" nos scripts. Leia "ajuda, aguarde".
28410 lhunath

8

o tempo limite é provavelmente a primeira abordagem a tentar. Você pode precisar de notificação ou outro comando para executar se o tempo limite for excedido. Depois de pesquisar e experimentar, criei este script bash :

if 
    timeout 20s COMMAND_YOU_WANT_TO_EXECUTE;
    timeout 20s AS_MANY_COMMANDS_AS_YOU_WANT;
then
    echo 'OK'; #if you want a positive response
else
    echo 'Not OK';
    AND_ALTERNATIVE_COMMANDS
fi

5

Script simples com clareza de código. Salvar em /usr/local/bin/run:

#!/bin/bash

# run
# Run command with timeout $1 seconds.

# Timeout seconds
timeout_seconds="$1"
shift

# PID
pid=$$

# Start timeout
(
  sleep "$timeout_seconds"
  echo "Timed out after $timeout_seconds seconds"
  kill -- -$pid &>/dev/null
) &
timeout_pid=$!

# Run
"$@"

# Stop timeout
kill $timeout_pid &>/dev/null

Tempo limite de um comando que demora muito:

$ run 2 sleep 10
Timed out after 2 seconds
Terminated
$

Termina imediatamente para um comando que é concluído:

$ run 10 sleep 2
$

3

Se você já sabe o nome do programa (vamos supor program) terminar após o tempo limite (como um exemplo de 3segundos), posso contribuir com uma solução alternativa simples e um tanto suja:

(sleep 3 && killall program) & ./program

Isso funciona perfeitamente se eu chamar processos de benchmark com chamadas do sistema.


2
Isso mata outros processos que usam o nome e não mata o processo com o nome dado se ele mudar de nome (por exemplo, escrevendo em argv[0], talvez com outros hacks para criar mais espaço).
Jed

Encontrei uma variação disso útil ao tentar parar os contêineres do docker após um certo período de tempo. O cliente do docker parecia não aceitar TERM / INT / KILL de uma maneira que realmente parasse o contêiner que estava sendo executado em primeiro plano. Então, dar um nome ao contêiner e usá-lo (sleep 3 && docker stop <container>) &funcionou bem. Obrigado!
Mat Schaffer

sim, é sujo e limitado, mas pode ser melhorado como: { sleep 5 && kill -9 $(ps -fe | grep "program" | grep $$ | tr -s " " | cut -d" " -f2); } & SLEEPPID=$!; bash -c "program" && kill -9 $SLEEPPID"Dessa forma, ele matará apenas trabalhos no shell atual.
ton

2

Também há cratimeoutMartin Cracauer (escrito em C para sistemas Unix e Linux).

# cf. http://www.cons.org/cracauer/software.html
# usage: cratimeout timeout_in_msec cmd args
cratimeout 5000 sleep 1
cratimeout 5000 sleep 600
cratimeout 5000 tail -f /dev/null
cratimeout 5000 sh -c 'while sleep 1; do date; done'

"Em particular, preserva o comportamento do sinal". É bom ter essa opção!
system PAUSE

1

O OS X ainda não usa o bash 4, nem possui / usr / bin / timeout, então aqui está uma função que funciona no OS X sem home-brew ou macports semelhante a / usr / bin / timeout (baseado no Tino's responda). A validação de parâmetros, ajuda, uso e suporte para outros sinais são um exercício para o leitor.

# implement /usr/bin/timeout only if it doesn't exist
[ -n "$(type -p timeout 2>&1)" ] || function timeout { (
    set -m +b
    sleep "$1" &
    SPID=${!}
    ("${@:2}"; RETVAL=$?; kill ${SPID}; exit $RETVAL) &
    CPID=${!}
    wait %1
    SLEEPRETVAL=$?
    if [ $SLEEPRETVAL -eq 0 ] && kill ${CPID} >/dev/null 2>&1 ; then
      RETVAL=124
      # When you need to make sure it dies
      #(sleep 1; kill -9 ${CPID} >/dev/null 2>&1)&
      wait %2
    else
      wait %2
      RETVAL=$?
    fi
    return $RETVAL
) }

0

Em 99% dos casos, a resposta NÃO é implementar nenhuma lógica de tempo limite. Timeout lógica é em quase qualquer situação um sinal de aviso vermelho que algo outra coisa está errado e deve ser fixado vez .

Seu processo está travando ou interrompido após n segundos, às vezes? Então descubra o porquê e conserte isso.

Como um aparte, para fazer a solução do strager corretamente, você precisa usar a espera "$ SPID" em vez do fg 1, pois nos scripts você não tem controle de tarefas (e tentar ativá-la é estúpido). Além disso, o fg 1 depende do fato de você não ter iniciado outros trabalhos anteriormente no script, o que é uma suposição ruim a ser feita.


4
Com acesso a 100% da fonte (e a maioria do hardware, como comutadores de rede), eu concordaria que provavelmente existem soluções melhores do que um tempo limite. Mas quando um 'tlrbsf' é de código fechado, apenas binário, às vezes você precisa contornar essa limitação.
system PAUSE

@lhunath, "nos scripts você não tem controle de tarefas (e tentar ativá-lo é estúpido)" - Esclareça aqui: stackoverflow.com/questions/690266/…
system PAUSE

@system PAUSE: A resposta stackoverflow.com/questions/690266/… está correta, também comentei.
22710 lhunath

29
Não, o que você está dizendo não faz sentido. Existem muitos casos em que o tempo limite é uma boa opção, por exemplo, sempre que você precisar acessar a rede.
Nate Murray

Uma exceção: você está escrevendo scripts de teste para verificar se um software já em execução não possui um problema de tempo limite.
peterh - Restabelece Monica

0

Foi-me apresentado um problema para preservar o contexto do shell e permitir tempos limite, o único problema é que ele interromperá a execução do script no tempo limite - mas tudo bem com as necessidades que me foram apresentadas:

#!/usr/bin/env bash

safe_kill()
{
  ps aux | grep -v grep | grep $1 >/dev/null && kill ${2:-} $1
}

my_timeout()
{
  typeset _my_timeout _waiter_pid _return
  _my_timeout=$1
  echo "Timeout($_my_timeout) running: $*"
  shift
  (
    trap "return 0" USR1
    sleep $_my_timeout
    echo "Timeout($_my_timeout) reached for: $*"
    safe_kill $$
  ) &
  _waiter_pid=$!
  "$@" || _return=$?
  safe_kill $_waiter_pid -USR1
  echo "Timeout($_my_timeout) ran: $*"
  return ${_return:-0}
}

my_timeout 3 cd scripts
my_timeout 3 pwd
my_timeout 3 true  && echo true || echo false
my_timeout 3 false && echo true || echo false
my_timeout 3 sleep 10
my_timeout 3 pwd

com as saídas:

Timeout(3) running: 3 cd scripts
Timeout(3) ran: cd scripts
Timeout(3) running: 3 pwd
/home/mpapis/projects/rvm/rvm/scripts
Timeout(3) ran: pwd
Timeout(3) running: 3 true
Timeout(3) ran: true
true
Timeout(3) running: 3 false
Timeout(3) ran: false
false
Timeout(3) running: 3 sleep 10
Timeout(3) reached for: sleep 10
Terminated

é claro que eu assumo que havia um dir chamado scripts


0
#! /bin/bash
timeout=10
interval=1
delay=3
(
    ((t = timeout)) || :

    while ((t > 0)); do
        echo "$t"
        sleep $interval
        # Check if the process still exists.
        kill -0 $$ 2> /dev/null || exit 0
        ((t -= interval)) || :
    done

    # Be nice, post SIGTERM first.
    { echo SIGTERM to $$ ; kill -s TERM $$ ; sleep $delay ; kill -0 $$ 2> /dev/null && { echo SIGKILL to $$ ; kill -s KILL $$ ; } ; }
) &

exec "$@"

@ Tino Desculpe, esqueci por que alterei a linha de encerramento do processo e por que pensei que isso era importante para compartilhar. Pena que não escrevi isso. Talvez eu tenha achado que precisava fazer uma pausa antes de verificar o êxito do kill -s TERM. O script do ano de 2008 do livro de receitas parece verificar o estado do processo imediatamente após o envio do SIGTERM, possivelmente levando a um erro ao tentar enviar o SIGKILL para um processo que morreu.
precisa saber é o seguinte

0

Meu problema foi talvez um pouco diferente: inicio um comando via ssh em uma máquina remota e quero matar o shell e os childs se o comando travar.

Agora uso o seguinte:

ssh server '( sleep 60 && kill -9 0 ) 2>/dev/null & my_command; RC=$? ; sleep 1 ; pkill -P $! ; exit $RC'

Dessa forma, o comando retorna 255 quando houve um tempo limite ou o código de retorno do comando em caso de êxito

Observe que os processos de eliminação de uma sessão ssh são tratados de forma diferente de um shell interativo. Mas você também pode usar a opção -t para ssh para alocar um pseudo terminal, para que ele funcione como um shell interativo


0

Aqui está uma versão que não depende de gerar um processo filho - eu precisava de um script independente que incorporasse essa funcionalidade. Ele também faz um intervalo de pesquisa fracionário, para que você possa pesquisar mais rapidamente. o tempo limite teria sido preferido - mas estou preso em um servidor antigo

# wait_on_command <timeout> <poll interval> command
wait_on_command()
{
    local timeout=$1; shift
    local interval=$1; shift
    $* &
    local child=$!

    loops=$(bc <<< "($timeout * (1 / $interval)) + 0.5" | sed 's/\..*//g')
    ((t = loops))
    while ((t > 0)); do
        sleep $interval
        kill -0 $child &>/dev/null || return
        ((t -= 1))
    done

    kill $child &>/dev/null || kill -0 $child &>/dev/null || return
    sleep $interval
    kill -9 $child &>/dev/null
    echo Timed out
}

slow_command()
{
    sleep 2
    echo Completed normally
}

# wait 1 sec in 0.1 sec increments
wait_on_command 1 0.1 slow_command

# or call an external command
wait_on_command 1 0.1 sleep 10

-1

Uma maneira muito simplista:

# command & sleep 5; pkill -9 -x -f "command"

com pkill (opção -f ), você pode matar seu comando específico com argumentos ou especificar -n para evitar o processo antigo.


Você percebe que isso é essencialmente o que o OP tem em seu cargo e o que ele indica que não quer, certo? Porque sempre espera o atraso total do sono.
Etan Reisner

-1

Com base na resposta do @ loup ...

Se você deseja exceder o tempo limite de um processo e silenciar a saída kill job / pid, execute:

( (sleep 1 && killall program 2>/dev/null) &) && program --version 

Isso coloca o processo em segundo plano em um subshell para que você não veja a saída do trabalho.


-3

Eu tenho um trabalho cron que chama um script php e, algumas vezes, fica preso no script php. Esta solução foi perfeita para mim.

Eu uso:

scripttimeout -t 60 /script.php

1
O que é scripttimeout?
Rudolfbyker 22/07/19
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.