Come introdurre il timeout per gli script di shell?


35

Voglio eseguire uno script di shell che contiene un ciclo e può andare per sempre, cosa che non voglio che accada. Quindi devo introdurre un timeout per l'intero script.

Come posso introdurre un timeout per l'intero script della shell in SuSE?


4
Questa è una domanda molto vaga. Potete per favore approfondire che tipo di timeout? Vuoi un timeout su tutto lo script o un timeout su una singola funzione o comando? Cosa stai cercando di fare?
Tim Kennedy,

@TimKennedy: spero sia meglio adesso.
Radek,

1
Vedi anche Timeout in uno script di shell (non lo considero un duplicato a causa del mio requisito di portabilità che esclude strumenti come timeouto expect)
SO di Gilles- smetti di essere malvagio '18

Risposte:


30

Se GNU timeoutnon è disponibile è possibile utilizzare expect(Mac OS X, BSD, ... di solito non dispongono di strumenti e utilità GNU per impostazione predefinita).

################################################################################
# Executes command with a timeout
# Params:
#   $1 timeout in seconds
#   $2 command
# Returns 1 if timed out 0 otherwise
timeout() {

    time=$1

    # start the command in a subshell to avoid problem with pipes
    # (spawn accepts one command)
    command="/bin/sh -c \"$2\""

    expect -c "set echo \"-noecho\"; set timeout $time; spawn -noecho $command; expect timeout { exit 1 } eof { exit 0 }"    

    if [ $? = 1 ] ; then
        echo "Timeout after ${time} seconds"
    fi

}

Modifica esempio:

timeout 10 "ls ${HOME}"

Sembra buono. Come posso vedere l'output del comando eseguito sullo schermo?
Radek,

Salve, l'output dovrebbe essere visibile poiché stdout non viene reindirizzato da nessuna parte. Ho aggiunto un esempio per l'utilizzo. Nel mio caso stampa il contenuto della mia home directory come previsto. Quale comando non stampa alcun output?
Matteo,

Non l'ho chiamato correttamente. Funziona alla grande! Grazie. Solo non genera il numero di secondi dopo il timeout.
Radek,

@Radek Siamo spiacenti, risolto
Matteo

1
Vorrei sottolineare che se si utilizza il timeout GNU, lo stato di ritorno di timeoutse stesso è 124 nel caso di un timeout, altrimenti è lo stato di ritorno del comando (questo è menzionato nella risposta di Tim Kennedy).
QuasarDonkey l'

15

Grazie per il chiarimento.

Il modo più semplice per realizzare ciò che cerchi è eseguire lo script con il ciclo all'interno di un wrapper come il timeoutcomando dal pacchetto GNU Coreutils.

root@coraid-sp:~# timeout --help            
Usage: timeout [OPTION] DURATION COMMAND [ARG]...
   or: timeout [OPTION]
Start COMMAND, and kill it if still running after DURATION.

Mandatory arguments to long options are mandatory for short options too.
  -k, --kill-after=DURATION
                   also send a KILL signal if COMMAND is still running
                   this long after the initial signal was sent.
  -s, --signal=SIGNAL
                   specify the signal to be sent on timeout.
                   SIGNAL may be a name like 'HUP' or a number.
                   See `kill -l` for a list of signals
      --help     display this help and exit
      --version  output version information and exit

DURATION is an integer with an optional suffix:
`s' for seconds(the default), `m' for minutes, `h' for hours or `d' for days.

If the command times out, then exit with status 124.  Otherwise, exit
with the status of COMMAND.  If no signal is specified, send the TERM
signal upon timeout.  The TERM signal kills any process that does not
block or catch that signal.  For other processes, it may be necessary to
use the KILL (9) signal, since this signal cannot be caught.

Report timeout bugs to bug-coreutils@gnu.org
GNU coreutils home page: <http://www.gnu.org/software/coreutils/>
General help using GNU software: <http://www.gnu.org/gethelp/>
For complete documentation, run: info coreutils 'timeout invocation'

Alla fine, sarà molto più semplice che scrivere la propria funzione di timeout, che le shell tendono a non avere incorporato.


Non ho capito subito che avevo installato questo, perché è chiamato gtimeoutsul mio setup (OSX).
Joshua Goldberg,

7

Avvia un processo di watchdog dal tuo script per uccidere il suo genitore se dura troppo a lungo. Esempio:

# watchdog process
mainpid=$$
(sleep 5; kill $mainpid) &
watchdogpid=$!

# rest of script
while :
do
   ...stuff...
done
kill $watchdogpid

Questo script verrà terminato dal watchdog dopo cinque secondi.


6
Ma dovrai aspettare il timeout ogni volta. Nel tuo esempio il tuo script verrà sempre eseguito 5 secondi anche se lo script è molto più breve. Dovresti anche memorizzare il PID del cane da guardia e ucciderlo alla fine.
Matteo,

@Matteo Abbastanza giusto. Aggiunto.
Kyle Jones,

1
Nota che potrebbe essere più complicato di così, a seconda di cosa stanno facendo le ... cose ... Vedi Timing out in uno script di shell
Gilles 'SO- smetti di essere malvagio' il

leggi anche: per quanto tempo i PID possono essere considerati unici stackoverflow.com/questions/11323410/linux-pid-recycling
Florian Castellane

3

C'è anche cratimeoutdi Martin Cracauer.

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

2
#!/bin/sh

# Execute a command with a timeout

if [ "$#" -lt "2" ]; then
echo "Usage:   `basename $0` timeout_in_seconds command" >&2
echo "Example: `basename $0` 2 sleep 3 || echo timeout" >&2
exit 1
fi

cleanup()
{
trap - ALRM               #reset handler to default
kill -ALRM $a 2>/dev/null #stop timer subshell if running
kill $! 2>/dev/null &&    #kill last job
  exit 124                #exit with 124 if it was running
}

watchit()
{
trap "cleanup" ALRM
sleep $1& wait
kill -ALRM $$
}

watchit $1& a=$!         #start the timeout
shift                    #first param was timeout for sleep
trap "cleanup" ALRM INT  #cleanup after timeout
"$@"& wait $!; RET=$?    #start the job wait for it and save its return value
kill -ALRM $a            #send ALRM signal to watchit
wait $a                  #wait for watchit to finish cleanup
exit $RET                #return the value

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.