Domanda

Questa risposta A Comando da riga di comando per terminare automaticamente un comando dopo un certo periodo di tempo

propone un metodo a 1 riga per timeout un comando di lunga esecuzione dalla riga di comando di bash:

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

Ma è possibile che un dato comando "di lunga esecuzione" possa terminare prima del timeout.(Chiamiamolo un comando "tipicamente di lunga durata ma a volte veloce", o tlrbsf per divertimento.)

Quindi questo ingegnoso approccio di 1 riga presenta un paio di problemi.Prima il sleep non è condizionale, quindi imposta un limite inferiore indesiderato sul tempo impiegato per il completamento della sequenza.Considera 30 o 2 metri o anche 5 metri per il sonno, quando il tlrbsf il comando termina in 2 secondi: altamente indesiderabile.In secondo luogo, il kill è incondizionato, quindi questa sequenza tenterà di interrompere un processo non in esecuzione e di lamentarsene.

COSÌ...

C'è un modo? per eseguire il timeout di un comando tipicamente di lunga esecuzione ma a volte veloce ("tlrbsf") comandalo

  • ha un'implementazione bash (l'altra domanda ha già risposte Perl e C)
  • terminerà al primo dei due: tlrbsf chiusura del programma o timeout scaduto
  • non ucciderà i processi inesistenti/non in esecuzione (o, facoltativamente:non lo farà reclamo di una brutta uccisione)
  • non deve essere un 1-liner
  • può funzionare con Cygwin o Linux

...e, per ottenere punti bonus, esegue il tlrbsf comando in primo piano e qualsiasi processo "sonno" o aggiuntivo in background, in modo tale che stdin/stdout/stderr del tlrbsf il comando può essere reindirizzato, come se fosse stato eseguito direttamente?

Se è così, condividi il tuo codice.In caso contrario, spiegare il motivo.

Ho passato un po' di tempo cercando di hackerare l'esempio di cui sopra, ma sto raggiungendo il limite delle mie capacità di bash.

È stato utile?

Soluzione

Credo che questo sia esattamente ciò che stai chiedendo:

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 "$@"

Altri suggerimenti

Probabilmente si sta cercando il comando timeout in coreutils. Dal momento che è una parte di coreutils, è tecnicamente una soluzione C, ma è ancora coreutils. info timeout per maggiori dettagli. Ecco un esempio:

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

Questa soluzione funziona indipendentemente dalla modalità di monitoraggio di bash.Puoi utilizzare il segnale corretto per terminare your_command

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

L'osservatore uccide your_command dopo un determinato timeout;lo script attende l'attività lenta e termina l'osservatore.Notare che wait non funziona con processi figli di una shell diversa.

Esempi:

  • your_command viene eseguito per più di 2 secondi ed è stato terminato

tuo_comando interrotto

( 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 è terminato prima del timeout (20 secondi)

il tuo_comando è terminato

( 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

Ci si va:

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

è possibile cambiare il SIGINT e 10 come volete;)

preferisco "tempo limite", che ha un pacchetto almeno in Debian.

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

E 'un po' più bello del "timeout" coreutils perché la stampa qualcosa quando uccidendo il processo, e invia anche SIGKILL dopo qualche tempo di default.

Puoi farlo interamente con bash 4.3 e al di sopra:

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

  • Richiede Bash 4.3 wait -n

  • Restituisce 137 se il comando è stato terminato, altrimenti il ​​valore restituito del comando.
  • Funziona per tubi.(Non è necessario che tu vada in primo piano qui!)
  • Funziona anche con comandi o funzioni interni della shell.
  • Viene eseguito in una subshell, quindi nessuna esportazione di variabili nella shell corrente, mi dispiace.

Se non hai bisogno del codice di ritorno, questo può essere reso ancora più semplice:

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

Appunti:

  • A rigor di termini non è necessario il file ; In ; ), tuttavia rende le cose più coerenti con il ; }-caso.E il set +b probabilmente può anche essere lasciato da parte, ma è meglio prevenire che curare.

  • Eccetto per --forground (probabilmente) puoi implementare tutte le varianti timeout supporta. --preserve-status è un po' difficile, però.Questo è lasciato come esercizio per il lettore ;)

Questa ricetta può essere utilizzata "naturalmente" nel guscio (naturale come per flock fd):

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

Tuttavia, come spiegato sopra, non è possibile riesportare le variabili di ambiente nella shell che le racchiude in questo modo in modo naturale.

Modificare:

Esempio del mondo reale:Tempo scaduto __git_ps1 nel caso in cui impieghi troppo tempo (per cose come collegamenti SSHFS lenti):

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

Modifica2:Risoluzione del problema.ho notato che exit 137 non è necessario e rende _timeout inaffidabile allo stesso tempo.

Modifica3: git è un duro a morire, quindi ha bisogno di un doppio trucco per funzionare in modo soddisfacente.

Modifica4:Ho dimenticato un _ nel primo _timeout per l'esempio GIT del mondo reale.

Si veda anche la http://www.pixelbeat.org/scripts/timeout script funzionalità di cui è stato integrato in coreutils più recenti

Un po 'hacky, ma funziona. Non funziona se si dispone di altri processi in primo piano (ti prego, aiutami a risolvere questo problema!)

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

In realtà, penso che si può invertire, soddisfare i vostri criteri 'bonus':

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

timeout è probabilmente il primo approccio da provare. Potrebbe essere necessario notifica o un altro comando da eseguire se il tempo è scaduto. Dopo un po 'di ricerca e sperimentazione, sono arrivato fino a questo bash script:

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

sceneggiatura semplice con il codice di chiarezza. Salva /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

volte su un comando che viene eseguito troppo a lungo:

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

termina immediatamente per un comando che completa:

$ run 10 sleep 2
$

Per timeout del slowcommand dopo 1 secondo:

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

Se si conosce già il nome del programma (supponiamo program) per terminare dopo il timeout (come esempio 3 secondi), posso contribuire una soluzione alternativa semplice e un po 'sporca:

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

Questo funziona perfettamente se chiamo i processi di riferimento con le chiamate di sistema.

C'è anche cratimeout da Martin Cracauer (scritto in C per sistemi 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'

OS X non usare bash 4 ancora, né ha / usr / bin / timeout, quindi ecco una funzione che funziona su OS X senza birra fatta in casa o MacPorts che è simile a / usr / bin / timeout ( in base alla risposta del Tino). la convalida dei parametri, l'aiuto, l'uso, e il supporto per altri segnali sono un esercizio per il lettore.

# 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
) }

mi è stato presentato con un problema di preservare il contesto guscio e consentire timeout, l'unico problema con esso è si fermerà l'esecuzione dello script sul timeout - ma va bene con le esigenze mi è stato presentato:

#!/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

con le uscite:

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

Naturalmente suppongo ci fosse una directory chiamata scripts

#! /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 "$@"

Il mio problema era forse un po 'diverso: mi metto un comando tramite SSH su una macchina remota e voglio uccidere il guscio e bambino, se il comando si blocca

.

ora uso il seguente:

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

In questo modo il comando restituisce 255 quando c'era un timeout o il codice di ritorno del comando in caso di successo

Si prega di notare che uccidere i processi da una sessione SSH è gestito diverso da una shell interattiva. Ma è anche possibile utilizzare l'opzione -t a ssh di allocare una pseudo terminale, in modo che agisce come una shell interattiva

Ecco una versione che non si basa su generando un processo figlio - avevo bisogno di uno script autonomo che integrato questa funzionalità. Si fa anche un intervallo di polling frazionale, in modo da poter eseguire il polling più veloce. timeout sarebbe stato preferito - ma mi sono bloccato su un vecchio server

# 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

@ risposta di loup ...

Se si vuole timeout di un processo e mettere a tacere il lavoro uccidere uscita / pid, eseguire:

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

In questo modo il processo in background in una subshell in modo che non si vede l'uscita di lavoro.

Ho un lavoro cron che richiama uno script PHP e, alcune volte, rimane bloccata su script php. Questa soluzione era perfetto per me.

Io uso:

scripttimeout -t 60 /script.php

Nel 99% dei casi la risposta è di non implementare qualsiasi logica di timeout. logica Timeout è in quasi ogni situazione un segnale di pericolo rosso che qualcosa altro è sbagliato e deve essere fissato al posto .

è la vostra impiccagione processo o rottura dopo n secondi a volte? Poi scoprire perché e risolvere che, invece.

Per inciso, per fare giusta soluzione di Strager, è necessario utilizzare wait "$ SPID" invece di fg 1, dal momento che negli script non si ha il controllo di posti di lavoro (e cercando di accenderlo è stupido). Inoltre, fg 1 si basa sul fatto che non è stato avviato alcun altri lavori in precedenza nello script, che è una cattiva ipotesi di fare.

Un modo molto semplicistico:

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

con pkill (opzione -f ) si può uccidere il vostro comando specifico con argomenti o specificare -n per evitare di uccidere vecchio processo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top