Come posso limitare il tempo di esecuzione di uno script BASH

Ho uno script BASH da tempo in esecuzione che sto correndo sotto CYGWIN su Windows.

Vorrei limitare lo script a funzionare per 30 secondi e terminare automaticamente se supera questo limite. Idealmente, mi piacerebbe essere in grado di farlo a qualsiasi comando.

Per esempio:

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

o

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

Sotto cygwin il comando ulimit non sembra funzionare.

Sono aperto a qualsiasi idea.

Vedi lo script http://www.pixelbeat.org/scripts/timeout la cui funzionalità è stata integrata nei nuovi coreutils:

 #!/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 "[email protected]"& 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 

Il seguente script mostra come farlo usando le attività in background. La prima sezione uccide un processo di 60 secondi dopo il limite di 10 secondi. Il secondo tenta di uccidere un processo già uscito. Tieni presente che, se imposti il ​​tuo timeout molto alto, gli ID di processo potrebbero capitare e uccidere il processo sbagliato, ma questo è più di un problema teorico – il timeout dovrebbe essere molto grande e dovresti essere iniziando molti processi.

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

Ecco l’output sulla mia scatola Cygwin:

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

Se si desidera attendere solo fino al termine del processo, è necessario inserire un ciclo e controllare. Questo è leggermente meno accurato poiché il sleep 1 e gli altri comandi richiedono in realtà più di un secondo (ma non molto di più). Utilizzare questo script per sostituire la seconda sezione sopra (i comandi ” echo $proc ” e ” date ” sono per il debug, non mi aspetto di averli nella soluzione finale).

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

Fondamentalmente, esegue il ciclo, controllando se il processo è ancora in esecuzione ogni secondo. Altrimenti, esce dal ciclo con un valore speciale per non tentare e uccidere il bambino. Altrimenti scade e uccide il bambino.

Ecco l’output per 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 

e 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 

Dai un’occhiata a questo link . L’idea è che tu myscript.sh come sottoprocesso del tuo script e registrerai il suo PID, quindi lo ucciderò se viene eseguito troppo a lungo.

È ansible eseguire il comando come processo in background (cioè con “&”), utilizzare la variabile bash per “pid of last command run”, attendere per il tempo richiesto, quindi eseguire kill con quel pid.