¿Cómo limito el tiempo de ejecución de un script de BASH?

Tengo un script BASH de ejecución larga que estoy ejecutando bajo CYGWIN en Windows.

Me gustaría limitar el script para que se ejecute durante 30 segundos y terminar automáticamente si supera este límite. Idealmente, me gustaría poder hacer esto con cualquier comando.

Por ejemplo:

sh-3.2$ limittime -t 30 'myscript.sh' 

o

 sh-3.2$ limittime -t 30 'grep func *.c' 

Bajo cygwin, el comando ulimit no parece funcionar.

Estoy abierto a cualquier idea.

Consulte el script http://www.pixelbeat.org/scripts/timeout cuya funcionalidad se ha integrado en coreutils más nuevos:

 #!/bin/sh # Execute a command with a timeout # License: LGPLv2 # Author: # http://www.pixelbeat.org/ # Notes: # Note there is a timeout command packaged with coreutils since v7.0 # If the timeout occurs the exit status is 124. # There is an asynchronous (and buggy) equivalent of this # script packaged with bash (under /usr/share/doc/ in my distro), # which I only noticed after writing this. # I noticed later again that there is a C equivalent of this packaged # with satan by Wietse Venema, and copied to forensics by Dan Farmer. # Changes: # V1.0, Nov 3 2006, Initial release # V1.1, Nov 20 2007, Brad Greenlee  # Make more portable by using the 'CHLD' # signal spec rather than 17. # V1.3, Oct 29 2009, Ján Sáreník  # Even though this runs under dash,ksh etc. # it doesn't actually timeout. So enforce bash for now. # Also change exit on timeout from 128 to 124 # to match coreutils. # V2.0, Oct 30 2009, Ján Sáreník  # Rewritten to cover compatibility with other # Bourne shell implementations (pdksh, dash) 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 

La siguiente secuencia de comandos muestra cómo hacerlo usando tareas en segundo plano. La primera sección elimina un proceso de 60 segundos después del límite de 10 segundos. El segundo intenta matar un proceso que ya ha salido. Tenga en cuenta que, si establece un tiempo de espera realmente alto, los identificadores de proceso pueden renovarse y usted matará el proceso incorrecto, pero esto es más un problema teórico: el tiempo de espera debería ser muy grande y tendría que ser comenzando muchos procesos

 #!/usr/bin/bash sleep 60 & pid=$! sleep 10 kill -9 $pid sleep 3 & pid=$! sleep 10 kill -9 $pid 

Aquí está la salida en mi caja de Cygwin:

 $ ./limit10 ./limit10: line 9: 4492 Killed sleep 60 ./limit10: line 11: kill: (4560) - No such process 

Si solo desea esperar hasta que el proceso haya finalizado, debe ingresar un ciclo y verificar. Esto es un poco menos preciso ya que sleep 1 y los otros comandos realmente tomarán más de un segundo (pero no mucho más). Utilice esta secuencia de comandos para reemplazar la segunda sección anterior (los comandos ” echo $proc ” y ” date ” son para la depuración, no esperaría tenerlos en la solución final).

 #!/usr/bin/bash date sleep 3 & pid=$! ((lim = 10)) while [[ $lim -gt 0 ]] ; do sleep 1 proc=$(ps -ef | awk -v pid=$pid '$2==pid{print}{}') echo $proc ((lim = lim - 1)) if [[ -z "$proc" ]] ; then ((lim = -9)) fi done date if [[ $lim -gt -9 ]] ; then kill -9 $pid fi date 

Básicamente bucles, verificando si el proceso aún se está ejecutando cada segundo. Si no, sale del ciclo con un valor especial para no intentar matar al niño. De lo contrario, se agota y mata al niño.

Aquí está la salida para un sleep 3 :

 Mon Feb 9 11:10:37 WADT 2009 pax 4268 2476 con 11:10:37 /usr/bin/sleep pax 4268 2476 con 11:10:37 /usr/bin/sleep Mon Feb 9 11:10:41 WADT 2009 Mon Feb 9 11:10:41 WADT 2009 

y un sleep 60 :

 Mon Feb 9 11:11:51 WADT 2009 pax 4176 2600 con 11:11:51 /usr/bin/sleep pax 4176 2600 con 11:11:51 /usr/bin/sleep pax 4176 2600 con 11:11:51 /usr/bin/sleep pax 4176 2600 con 11:11:51 /usr/bin/sleep pax 4176 2600 con 11:11:51 /usr/bin/sleep pax 4176 2600 con 11:11:51 /usr/bin/sleep pax 4176 2600 con 11:11:51 /usr/bin/sleep pax 4176 2600 con 11:11:51 /usr/bin/sleep pax 4176 2600 con 11:11:51 /usr/bin/sleep pax 4176 2600 con 11:11:51 /usr/bin/sleep Mon Feb 9 11:12:03 WADT 2009 Mon Feb 9 11:12:03 WADT 2009 ./limit10: line 20: 4176 Killed sleep 60 

Mira este enlace . La idea es que ejecute myscript.sh como un subproceso de su script y grabe su PID, luego myscript.sh si se ejecuta demasiado tiempo.

Puede ejecutar el comando como un trabajo en segundo plano (es decir, con “&”), usar la variable bash para “pid del último comando ejecutado”, dormir durante el tiempo requerido, luego ejecutar kill con ese pid.