J'ai un script bash qui lance un processus enfant qui plante (en fait, se bloque) de temps en temps et sans raison apparente (source fermée, donc je ne peux pas faire grand-chose à ce sujet). En conséquence, j'aimerais pouvoir lancer ce processus pendant un laps de temps donné et le tuer s'il ne revient pas avec succès après un laps de temps donné.
Existe-t-il un moyen simple et robuste d'y parvenir en utilisant bash?
PS: dites-moi si cette question est mieux adaptée à serverfault ou superuser.
Réponses:
(Comme vu dans: BASH FAQ entrée # 68: "Comment exécuter une commande et la faire abandonner (délai d'expiration) après N secondes?" )
Si cela ne vous dérange pas de télécharger quelque chose, utilisez
timeout
(sudo apt-get install timeout
) et utilisez-le comme: (la plupart des systèmes l'ont déjà installé, sinon utilisezsudo apt-get install coreutils
)Si vous ne voulez pas télécharger quelque chose, faites ce que timeout fait en interne:
Dans le cas où vous souhaitez faire un timeout pour un code bash plus long, utilisez la deuxième option en tant que telle:
la source
cmdpid=$BASHPID
ne prendra pas le pid du shell appelant mais le (premier) sous-shell qui est démarré()
. La(sleep
chose ... appelle un deuxième sous-shell dans le premier sous-shell pour attendre 10 secondes en arrière-plan et tuer le premier sous-shell qui, après avoir lancé le processus de sous-shell tueur, procède à l'exécution de sa charge de travail ...timeout
fait partie de GNU coreutils, il devrait donc déjà être installé dans tous les systèmes GNU.timeout
fait maintenant partie des coreutils.ou pour obtenir également les codes de sortie:
la source
kill -9
avant d'essayer les signaux qu'un processus peut traiter en premier.dosmth
termine dans 2 secondes, un autre processus prend l'ancien pid et vous tuez le nouveau?la source
sleep 999
ici) finissait souvent plus vite que le sleep imposé (sleep 10
)? Et si je souhaite lui donner une chance jusqu'à 1 minute, 5 minutes? Et si j'avais un tas de cas de ce genre dans mon script :)J'ai également eu cette question et j'ai trouvé deux autres choses très utiles:
J'utilise donc quelque chose comme ça sur la ligne de commande (OSX 10.9):
Comme il s'agit d'une boucle, j'ai inclus un "sleep 0.2" pour garder le CPU au frais. ;-)
(BTW: ping est de toute façon un mauvais exemple, vous utiliseriez simplement l'option intégrée "-t" (timeout).)
la source
En supposant que vous avez (ou pouvez facilement créer) un fichier pid pour suivre le pid de l'enfant, vous pouvez alors créer un script qui vérifie le modtime du fichier pid et tue / réapparaît le processus si nécessaire. Ensuite, placez simplement le script dans crontab pour qu'il s'exécute approximativement à la période dont vous avez besoin.
Faites-moi savoir si vous avez besoin de plus de détails. Si cela ne semble pas répondre à vos besoins, qu'en est-il du parvenu?
la source
Une façon consiste à exécuter le programme dans un sous-shell et à communiquer avec le sous-shell via un canal nommé avec la
read
commande. De cette façon, vous pouvez vérifier l'état de sortie du processus en cours d'exécution et le communiquer via le canal.Voici un exemple d'expiration de la
yes
commande après 3 secondes. Il obtient le PID du processus en utilisantpgrep
(peut-être ne fonctionne que sous Linux). Il y a aussi un problème avec l'utilisation d'un tube en ce qu'un processus ouvrant un tube en lecture se bloque jusqu'à ce qu'il soit également ouvert en écriture, et vice versa. Donc, pour éviter que laread
commande ne se bloque, j'ai "calé" ouvert le tube pour la lecture avec un sous-shell d'arrière-plan. (Une autre façon d'éviter un gel pour ouvrir le tube en lecture-écriture, c'estread -t 5 <>finished.pipe
-à- dire - cependant, cela peut également ne pas fonctionner sauf avec Linux.)la source
Voici une tentative qui essaie d'éviter de tuer un processus après sa sortie, ce qui réduit le risque de tuer un autre processus avec le même ID de processus (bien qu'il soit probablement impossible d'éviter complètement ce type d'erreur).
Utilisez like
run_with_timeout 3 sleep 10000
, qui s'exécutesleep 10000
mais le termine après 3 secondes.C'est comme d'autres réponses qui utilisent un processus de temporisation en arrière-plan pour tuer le processus enfant après un délai. Je pense que c'est presque la même chose que la réponse étendue de Dan ( https://stackoverflow.com/a/5161274/1351983 ), sauf que le shell timeout ne sera pas tué s'il est déjà terminé.
Une fois ce programme terminé, il y aura encore quelques processus de "sommeil" en cours d'exécution, mais ils devraient être inoffensifs.
Cela peut être une meilleure solution que mon autre réponse car elle n'utilise pas la fonctionnalité de shell non portable
read -t
et ne l'utilise paspgrep
.la source
(exec sh -c "$*") &
etsh -c "$*" &
? Plus précisément, pourquoi utiliser le premier au lieu du second?Voici la troisième réponse que j'ai soumise ici. Celui-ci gère les interruptions de signal et nettoie les processus d'arrière-plan lorsqu'il
SIGINT
est reçu. Il utilise l' astuce$BASHPID
etexec
utilisée dans la réponse du haut pour obtenir le PID d'un processus (dans ce cas$$
dans unesh
invocation). Il utilise un FIFO pour communiquer avec un sous-shell qui est responsable de la mise à mort et du nettoyage. (C'est comme le tube dans ma deuxième réponse , mais avoir un tube nommé signifie que le gestionnaire de signal peut également y écrire.)J'ai essayé d'éviter les conditions de course autant que possible. Cependant, une source d'erreur que je n'ai pas pu supprimer est lorsque le processus se termine presque en même temps que le délai d'expiration. Par exemple,
run_with_timeout 2 sleep 2
ourun_with_timeout 0 sleep 0
. Pour moi, ce dernier donne une erreur:car il essaie de tuer un processus qui est déjà sorti de lui-même.
la source