Cette réponse à la commande de ligne de commande pour tuer automatiquement une commande après un certain temps
propose une méthode sur une ligne pour expirer une commande de longue durée à partir de la ligne de commande bash:
( /path/to/slow command with options ) & sleep 5 ; kill $!
Mais il est possible qu'une commande "de longue durée" donnée se termine plus tôt que le délai d'expiration. (Appelons-la une commande "généralement longue mais parfois rapide" ou tlrbsf pour le plaisir.)
Donc, cette approche astucieuse à 1 doublure a quelques problèmes. Tout d'abord, le sleep
n'est pas conditionnel, ce qui définit une limite inférieure indésirable sur le temps nécessaire à la séquence pour se terminer. Considérez 30s ou 2m ou même 5m pour le sommeil, lorsque la commande tlrbsf se termine en 2 secondes - hautement indésirable. Deuxièmement, le kill
est inconditionnel, donc cette séquence tentera de tuer un processus non en cours et de se plaindre.
Alors...
Existe-t-il un moyen de temporiser une commande généralement longue mais parfois rapide ( "tlrbsf" ) qui
- a une implémentation bash (l'autre question a déjà des réponses Perl et C)
- se terminera à la première des deux: fin du programme tlrbsf , ou délai d'expiration
- ne tuera pas les processus non existants / non en cours d'exécution (ou, facultativement: ne se plaindra pas d'un mauvais kill)
- ne doit pas être un doublure
- peut fonctionner sous Cygwin ou Linux
... et, pour les points bonus, exécute la commande tlrbsf au premier plan et tout processus "sleep" ou supplémentaire en arrière-plan, de telle sorte que la commande stdin / stdout / stderr de la commande tlrbsf peut être redirigée, comme si elle avait été courir directement?
Si oui, veuillez partager votre code. Sinon, veuillez expliquer pourquoi.
J'ai passé un certain temps à essayer de pirater l'exemple susmentionné, mais j'atteins la limite de mes compétences bash.
la source
timeout
utilitaire GNU ?timeout
c'est super! vous pouvez même utiliser avec plusieurs commandes (script multi-lignes): stackoverflow.com/a/61888916/658497Réponses:
Je pense que c'est précisément ce que vous demandez:
http://www.bashcookbook.com/bashinfo/source/bash-4.0/examples/scripts/timeout3
la source
Vous recherchez probablement la
timeout
commande dans coreutils. Puisqu'il fait partie de coreutils, c'est techniquement une solution C, mais c'est toujours coreutils.info timeout
pour plus de détails. Voici un exemple:la source
gtimeout
brew install coreutils
, et vous pouvez ensuite l'utilisergtimeout
.Cette solution fonctionne quel que soit le mode de surveillance bash. Vous pouvez utiliser le signal approprié pour terminer votre_commande
L'observateur tue votre_commande après un délai d'expiration donné; le script attend la tâche lente et termine l'observateur. Notez que
wait
cela ne fonctionne pas avec les processus qui sont des enfants d'un shell différent.Exemples:
la source
wait
renvoie l'état de sortie du processus en attente. Donc, si votre commande se termine dans le temps imparti mais avec un état de sortie différent de zéro, la logique ici se comportera comme elle a expiré, c'est-à-dire imprimeryour_command interrupted
. Au lieu de cela, vous pouvez fairewait
sansif
et vérifier si le$watcher
pid existe toujours, si c'est le cas, vous savez que vous n'avez pas dépassé le délai d'expiration.kill
dans un cas, maispkill
dans l'autre. J'avais besoin d'utiliser lespkill
deux pour faire fonctionner correctement. Je m'attends à ce que si vous encapsulez une commande()
, vous devrez alors l'utiliserpkill
pour la tuer. Mais peut-être que cela fonctionne différemment s'il n'y a qu'une seule commande à l'intérieur du()
.Voilà:
vous pouvez changer le
SIGINT
et10
comme vous le souhaitez;)la source
coreutils
paquet sur FreeBSD.Vous pouvez le faire entièrement avec
bash 4.3
et au-dessus:_timeout 5 longrunning_command args
{ _timeout 5 producer || echo KABOOM $?; } | consumer
producer | { _timeout 5 consumer1; consumer2; }
Exemple:
{ while date; do sleep .3; done; } | _timeout 5 cat | less
Nécessite Bash 4.3 pour
wait -n
Si vous n'avez pas besoin du code retour, cela peut être rendu encore plus simple:
Remarques:
Au sens strict , vous n'avez pas besoin
;
dans; )
, mais il fait chose plus cohérente à l'; }
-case. Et leset +b
peut aussi être laissé de côté, mais mieux vaut prévenir que guérir.À l'exception de
--forground
(probablement), vous pouvez implémenter toutes les variantes detimeout
supports.--preserve-status
est un peu difficile, cependant. Ceci est laissé comme un exercice pour le lecteur;)Cette recette peut être utilisée "naturellement" dans la coquille (aussi naturelle que pour
flock fd
):Cependant, comme expliqué ci-dessus, vous ne pouvez pas réexporter naturellement les variables d'environnement dans le shell englobant de cette façon.
Éditer:
Exemple réel: Délai d'attente
__git_ps1
au cas où cela prendrait trop de temps (pour des choses comme les liens SSHFS lents):Edit2: Bugfix. J'ai remarqué que ce
exit 137
n'est pas nécessaire et rend_timeout
peu fiable en même temps.Edit3:
git
est un die-hard, il a donc besoin d'un double truc pour fonctionner de manière satisfaisante.Edit4: Oublié un
_
dans le premier_timeout
pour l'exemple GIT du monde réel.la source
cc. The 'wait' builtin has a new '-n' option to wait for the next child to change status.
De: tiswww.case.edu/php/chet/bash/NEWSJe préfère "timelimit", qui a au moins un paquet dans debian.
http://devel.ringlet.net/sysutils/timelimit/
Il est un peu plus agréable que le "timeout" coreutils car il imprime quelque chose lors de l'arrêt du processus, et il envoie également SIGKILL après un certain temps par défaut.
la source
timelimit -t1 ./a_forking_prog
ne tue qu'un des deux processus), mais le timeout fonctionne.Pour expirer
slowcommand
après 1 seconde:timeout 1 slowcommand || echo "I failed, perhaps due to time out"
Pour déterminer si la commande a expiré ou a échoué pour ses propres raisons, vérifiez si le code d'état est 124:
Notez que lorsque l'état de sortie est 124, vous ne savez pas s'il a expiré en raison de votre
timeout
commande, ou si la commande elle-même s'est terminée en raison d'une logique de temporisation interne qui lui est propre, puis a renvoyé 124. Vous pouvez supposer en toute sécurité dans les deux cas , cependant, qu'un délai d'attente d'une certaine sorte s'est produit.la source
Voir également le script http://www.pixelbeat.org/scripts/timeout dont la fonctionnalité a été intégrée dans les coreutils plus récents
la source
Un peu hacky, mais ça marche. Ne fonctionne pas si vous avez d'autres processus de premier plan (veuillez m'aider à résoudre ce problème!)
En fait, je pense que vous pouvez l'inverser en répondant à vos critères de bonus:
la source
le timeout est probablement la première approche à essayer. Il se peut que vous ayez besoin d'une notification ou d'une autre commande pour l'exécuter si elle expire. Après un peu de recherche et d'expérimentation, j'ai trouvé ce script bash :
la source
Script simple avec clarté de code. Enregistrer dans
/usr/local/bin/run
:Expire une commande trop longue:
Se termine immédiatement pour une commande qui se termine:
la source
Si vous connaissez déjà le nom du programme (supposons
program
) de se terminer après le délai d'expiration (par exemple en3
secondes), je peux apporter une solution alternative simple et quelque peu sale:Cela fonctionne parfaitement si j'appelle des processus de référence avec des appels système.
la source
argv[0]
, peut-être avec d'autres hacks pour faire plus de place).(sleep 3 && docker stop <container>) &
a bien fonctionné. Merci!{ sleep 5 && kill -9 $(ps -fe | grep "program" | grep $$ | tr -s " " | cut -d" " -f2); } & SLEEPPID=$!; bash -c "program" && kill -9 $SLEEPPID"
cette façon, il ne tuera que les travaux dans le shell actuel.Il y a aussi
cratimeout
par Martin Cracauer (écrit en C pour les systèmes Unix et Linux).la source
OS X n'utilise pas encore bash 4, ni n'a / usr / bin / timeout, alors voici une fonction qui fonctionne sur OS X sans home-brew ou macports qui est similaire à / usr / bin / timeout (basé sur Tino's répondre). La validation des paramètres, l'aide, l'utilisation et la prise en charge d'autres signaux sont un exercice pour le lecteur.
la source
Dans 99% des cas, la réponse n'est PAS d'implémenter une logique de temporisation. La logique de temporisation est dans presque toutes les situations un signe d'avertissement rouge que quelque chose d' autre ne va pas et devrait être corrigé à la place .
Votre processus est-il parfois suspendu ou interrompu après n secondes? Découvrez ensuite pourquoi et corrigez cela à la place.
En passant, pour faire correctement la solution de strager, vous devez utiliser attendre "$ SPID" au lieu de fg 1, car dans les scripts, vous n'avez pas de contrôle de travail (et essayer de l'activer est stupide). De plus, fg 1 repose sur le fait que vous n'avez démarré aucun autre travail auparavant dans le script, ce qui est une mauvaise hypothèse à faire.
la source
On m'a présenté un problème pour préserver le contexte du shell et autoriser les délais d'expiration, le seul problème avec cela est qu'il arrêtera l'exécution du script sur le délai d'expiration - mais cela convient aux besoins qui m'ont été présentés:
avec les sorties:
bien sûr, je suppose qu'il y avait un dir appelé
scripts
la source
la source
Mon problème était peut-être un peu différent: je lance une commande via ssh sur une machine distante et je veux tuer le shell et les enfants si la commande se bloque.
J'utilise maintenant les éléments suivants:
De cette façon, la commande retourne 255 en cas d'expiration ou le code retour de la commande en cas de succès
Veuillez noter que la suppression des processus d'une session ssh est gérée différemment d'un shell interactif. Mais vous pouvez également utiliser l'option -t pour ssh pour allouer un pseudo terminal, il agit donc comme un shell interactif
la source
Voici une version qui ne repose pas sur la création d'un processus enfant - j'avais besoin d'un script autonome qui incorporait cette fonctionnalité. Il effectue également un intervalle d'interrogation fractionné, de sorte que vous pouvez interroger plus rapidement. le délai aurait été préféré - mais je suis bloqué sur un ancien serveur
la source
Une manière très simpliste:
avec pkill (option -f ), vous pouvez tuer votre commande spécifique avec des arguments ou spécifier -n pour éviter de tuer l'ancien processus.
la source
S'appuyer sur la réponse de @ loup ...
Si vous souhaitez expirer un processus et arrêter la sortie du job kill / pid, exécutez:
Cela met le processus en arrière-plan dans un sous-shell afin que vous ne voyiez pas la sortie du travail.
la source
J'ai un travail cron qui appelle un script php et, parfois, il se bloque sur le script php. Cette solution était parfaite pour moi.
J'utilise:
la source
scripttimeout
?