2009-02-09 20 views
16

J'ai un script BASH de longue durée que j'utilise sous CYGWIN sous Windows.Comment limiter la durée d'exécution d'un script BASH?

Je voudrais limiter le script à exécuter pendant 30 secondes et se terminer automatiquement s'il dépasse cette limite. Idéalement, j'aimerais pouvoir le faire à n'importe quelle commande.

Par exemple:

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

ou

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

sous Cygwin la commande ulimit ne semble pas fonctionner.

Je suis ouvert à toutes les idées.

+0

@jm, voir ma réponse mise à jour pour savoir comment arrêter d'attendre au plus tôt o f timeout et child-exit-normalement. – paxdiablo

+3

Question similaire, quelques réponses différentes: http://stackoverflow.com/questions/687948 –

Répondre

14

Voir le script http://www.pixelbeat.org/scripts/timeout dont la fonctionnalité a été intégrée dans les nouveaux 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 <[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

C'est un bon script. J'ai aussi bien travaillé sous CYGWIN. –

+2

ce script ne fonctionnera pas si vous utilisez la redirection stdin - quelque chose comme: ./time_limit.sh cat siemanko

2

Vous pouvez exécuter la commande comme un travail de fond (par exemple avec « & »), utilisez la variable bash pour « pid de la dernière exécution de la commande, » le sommeil de la quantité requise de temps, puis exécutez kill avec ce pid.

5

Découvrez this link. L'idée est juste que vous exécuteriez myscript.sh en tant que sous-processus de votre script et enregistreriez son PID, puis le tueriez s'il est trop long.

+0

Je ne pouvais pas obtenir cet exemple à exécuter sur Cygwin. J'ai: sh: ligne 46: kill: SIGUSR1: spécification de signal invalide –

+0

Est-ce que cela fonctionne si vous remplacez SIGUSR1 par SIGTERM? –

+0

Cette solution semble en fait bizarre. Il démarre les tâches chronométrées, puis un sous-shell séparé pour dormir et envoyer USR1 au shell en cours d'exécution. Pourquoi ne pas dormir dans la coquille? – paxdiablo

12

Le script suivant montre comment procéder en utilisant des tâches d'arrière-plan. La première section tue un processus de 60 secondes après la limite de 10 secondes. La seconde tente de tuer un processus déjà sorti. Gardez à l'esprit que, si vous définissez votre délai d'attente très élevé, les identifiants de processus risquent de se renverser et vous tiendrez le mauvais processus, mais il s'agit plus d'un problème théorique - le délai devrait être très élevé et vous auriez démarrer un lot des processus.

#!/usr/bin/bash 

sleep 60 & 
pid=$! 
sleep 10 
kill -9 $pid 

sleep 3 & 
pid=$! 
sleep 10 
kill -9 $pid 

est ici la sortie sur ma boîte Cygwin:

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

Si vous voulez attendre que jusqu'à ce que le processus est terminé, vous devez entrer une boucle et vérifier. Ceci est légèrement moins précis puisque sleep 1 et les autres commandes prendront réellement plus d'une seconde (mais pas beaucoup plus). Utilisez ce script pour remplacer la deuxième section ci-dessus (les commandes "echo $proc" et "date" sont pour le débogage, je ne m'attendrais pas à les avoir dans la solution finale). Il boucle fondamentalement, vérifiant si le processus est toujours en cours d'exécution toutes les secondes.

Sinon, il quitte la boucle avec une valeur spéciale pour ne pas essayer de tuer l'enfant. Sinon, il expire et tue l'enfant.

est ici la sortie pour une 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 

et 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

C'est bien, mais la longueur minimale que le processus peut exécuter est maintenant le timeout. C'est exact pour la façon dont j'ai formulé la question, cependant. –

+0

@jm, voir ma réponse mise à jour. – paxdiablo

+1

Ack, n'utilisez pas kill -9 sauf si c'est absolument nécessaire! SIGKILL ne peut pas être piégé de sorte que le programme tué ne puisse exécuter aucune routine d'arrêt, par ex. effacer les fichiers temporaires. Essayez d'abord HUP (1), puis INT (2), puis QUIT (3). – andrewdotn