¿Cómo introducir el tiempo de espera para las secuencias de comandos de shell?

35

Quiero ejecutar un script de shell que tenga un bucle y pueda continuar para siempre, lo que no quiero que suceda. Entonces necesito introducir un tiempo de espera para todo el script.

¿Cómo puedo introducir un tiempo de espera para todo el script de shell en SuSE?

Radek
fuente
44
Esta es una pregunta muy vaga. ¿Puedes por favor explicar qué tipo de tiempo de espera? ¿Desea un tiempo de espera en todo el script o un tiempo de espera en una sola función o comando? ¿Que estás tratando de hacer?
Tim Kennedy
@TimKennedy: espero que sea mejor ahora.
Radek
1
Consulte también Tiempo de espera en un script de shell (no lo considero un duplicado debido a mi requisito de portabilidad que excluye herramientas como timeouto expect)
Gilles 'SO- deja de ser malo'

Respuestas:

30

Si GNU timeoutno está disponible, puede usarlo expect(Mac OS X, BSD, ... generalmente no tiene herramientas y utilidades GNU por defecto).

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

}

Editar ejemplo:

timeout 10 "ls ${HOME}"
Matteo
fuente
Se ve bien. ¿Cómo puedo ver la salida del comando ejecutado en la pantalla?
Radek
Hola, la salida debe ser visible ya que stdout no se redirige a ningún lado. Agregué un ejemplo para el uso. En mi caso, imprime el contenido de mi directorio de inicio como se esperaba. ¿Qué comando no está imprimiendo ningún resultado?
Matteo
No lo llamé correctamente. ¡Funciona genial! Gracias. Solo que no genera un número de segundos después del tiempo de espera.
Radek
@Radek Lo siento, arreglado
Matteo
1
Me gustaría resaltar que si se usa el tiempo de espera de GNU, el estado de retorno de timeoutsí mismo es 124 en el caso de un tiempo de espera, de lo contrario es el estado de retorno del comando (esto se menciona en la respuesta de Tim Kennedy).
QuasarDonkey
15

Gracias por la aclaración.

La forma más fácil de lograr lo que busca es ejecutar su script con el bucle dentro de un contenedor como el timeoutcomando del paquete 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 [email protected]
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'

Al final, será mucho más fácil que escribir su propia función de tiempo de espera, que las conchas tienden a no tener incorporado.

Tim Kennedy
fuente
No me di cuenta de inmediato de que tenía esto instalado, porque está llamado gtimeouten mi configuración (OSX).
Joshua Goldberg el
7

Inicie un proceso de vigilancia desde su script para matar a su padre si se ejecuta demasiado tiempo. Ejemplo:

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

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

El guardián terminará este script después de cinco segundos.

Kyle Jones
fuente
66
Pero tendrá que esperar el tiempo de espera cada vez. En su ejemplo, su script siempre se ejecutará 5 segundos, incluso si el script es mucho más corto. También debe almacenar el PID del perro guardián y matarlo al final.
Matteo
@Matteo lo suficientemente justo. Adicional.
Kyle Jones
1
Tenga en cuenta que puede ser más complicado que eso, dependiendo de lo que esté haciendo ... las cosas ... Vea Tiempo de espera en un guión de shell
Gilles 'SO- deja de ser malvado'
lea también: ¿durante cuánto tiempo pueden considerarse los PID únicos stackoverflow.com/questions/11323410/linux-pid-recycling
Florian Castellane
3

También hay cratimeoutde 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'
jackz
fuente
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
Vivek Ragupathy
fuente