2009-02-09 18 views
16

Possiedo uno script BASH a esecuzione prolungata eseguito in CYGWIN su Windows.Come limitare il tempo di esecuzione di uno script BASH

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.

Ad 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.

+0

@ jm, guarda la mia risposta aggiornata su come smettere di aspettare al più presto o f timeout e child-exit-normalmente. – paxdiablo

+3

Domanda simile, alcune risposte diverse: http://stackoverflow.com/questions/687948 –

risposta

14

vedere lo script http://www.pixelbeat.org/scripts/timeout la funzionalità di cui è stato integrato nel coreutils più recenti:

#!/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 <[email protected]> 
#      Make more portable by using the 'CHLD' 
#      signal spec rather than 17. 
# V1.3, Oct 29 2009, Ján Sáreník <[email protected]> 
#      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 <[email protected]> 
#      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 
+0

Questa è una bella sceneggiatura. Funzionava bene anche sotto CYGWIN. –

+2

questo script non funzionerà se stai usando il reindirizzamento stdin - qualcosa come: ./time_limit.sh cat siemanko

2

È possibile eseguire il comando come processo in background (cioè con "&"), utilizzare la variabile bash per 'pid di ultima corsa di comando,' sonno per la quantità necessaria di tempo, quindi eseguire kill con quel pid.

5

Check out this link. L'idea è che eseguirai myscript.sh come sottoprocesso del tuo script e registrerai il suo PID, quindi lo uccideresti se viene eseguito troppo a lungo.

+0

Non ho potuto ottenere quel campione da eseguire su cygwin. Ho ottenuto: sh: riga 46: kill: SIGUSR1: specifica di segnale non valida –

+0

Funziona se si sostituisce SIGUSR1 con SIGTERM? –

+0

Questa soluzione sembra davvero bizzarra. Avvia le attività temporizzate, quindi una subshell separata per dormire e invia USR1 alla shell in esecuzione. Perché non solo dormire nella conchiglia? – paxdiablo

12

Il seguente script mostra come eseguire questa operazione utilizzando 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 tu avresti per iniziare un lotto di 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 casella di Cygwin:

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

Se si vuole solo aspettare fino a quando il processo è finito, è necessario inserire un ciclo e verificare. Questo è leggermente meno preciso dal sleep 1 e gli altri comandi in realtà impiegano 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 
+0

Questo è buono, ma la lunghezza minima che il processo può eseguire è ora il timeout. Questo è corretto per il modo in cui ho formulato la domanda, però. –

+0

@jm, vedere la mia risposta aggiornata. – paxdiablo

+1

Ack, non usare kill -9 a meno che non sia assolutamente necessario! SIGKILL non può essere intrappolato in modo che il programma ucciso non possa eseguire alcuna routine di arresto ad es. cancella i file temporanei. Prima prova HUP (1), poi INT (2), quindi ESCI (3). – andrewdotn

Problemi correlati