|
Publicité ' | |||||||||||||||||||||||
|
|
#1 |
|
Invité régulier
![]() Inscription : novembre 2007 Messages : 49 ![]() |
Bonjour,
Je souhaite lancer de manière automatisée plusieurs scripts perl les uns après les autres, sous Windows XP pro. La 1ère solution (qui fonctionne bien) est de les lancer à des heures précises en utilisant le gestionnaire de tâches de Windows. Le problème est que le 2eme script doit être lancé une fois le 1er script terminé. J'ai pensé résoudre le problème en utilisant les id des processus. J'ai testé le module Win32 :: Process :: List, il fournit en effet la liste des processus en exécution. Mais, lorsqu'un script perl est en train de s'exécuter, ce que l'on a comme information sur le processus c'est uniquement "perl.exe" et son id. Y a-t-il un moyen d'obtenir précisément le nom du script qui s'exécute ? Mais ce n'est peut-être pas la bonne voie. Si quelqu'un a déjà résolu ce genre de problème, je serai intéressé par les solutions proposées. Merci d'avance. Krys006 |
|
|
00
|
|
|
#2 |
|
Membre Expert
![]() Laurent R.Conseil - Consultant en systèmes d'information Inscription : mai 2012 Messages : 570 ![]() |
Pourquoi ne pas simplement enchaîner les différents programmes dans un script qui ne continue et lance le programme2 que quand le programme1 lui a rendu la main?
__________________
Sauf mention contraire explicite, les bouts de code que je poste en réponse à une question n'ont pas forcément été testés. |
|
|
00
|
|
|
#3 |
|
Invité régulier
![]() Inscription : novembre 2007 Messages : 49 ![]() |
J'ai pensé en premier à cette possibilité. Mais en procédant ainsi, le script principal est dépendant du script 1. En effet le script 1 télécharge des données à partir d'un serveur.
Quand tout se passe bien, le temps pour effectuer cette tâche est à peu près constant. Mais quand le temps de fonctionnement du script 1 se prolonge à cause de retard possible dans le téléchargement, voire quand il se bloque, le script principal est bloqué lui aussi. Or, je souhaiterai garder un contrôle externe sur les scripts à exécuter. Ainsi, si le temps imparti pour le script 1 est dépassé et que celui-ci est toujours lancé, le script principal le ferme et le relance, puis se ferme lui-même. Si le script 1 est terminé, alors le script principal lance le script 2, puis se ferme lui-même. Au cas où ce scénario se produirait plusieurs fois, le script principal garde le contrôle sur les autres scripts en s'exécutant toutes les X minutes en tant que tâche programmée. C'est la raison pour laquelle je préférerai avoir accès aux scripts en cours de fonctionnement. Mais ce n'est peut-être pas la bonne façon de procéder ? |
|
|
00
|
|
|
#4 |
|
Membre Expert
![]() Laurent R.Conseil - Consultant en systèmes d'information Inscription : mai 2012 Messages : 570 ![]() |
Hum, il faut probablement utiliser du multi-process (ou du multi-thread).
De cette façon, le père peut garder le contrôle du fils et savoir quand il se termine.
__________________
Sauf mention contraire explicite, les bouts de code que je poste en réponse à une question n'ont pas forcément été testés. |
|
|
00
|
|
|
#5 |
|
Invité régulier
![]() Inscription : novembre 2007 Messages : 49 ![]() |
Merci pour ces propositions. Je ne connais pas bien le fonctionnement des scripts perl en mode multi-thread, alors je vais creuser un peu.
S'il existe un bon tutoriel sur la question, je suis preneur. Cependant, est-il facile de transformer un script classique en script multi-thread s'il n'a pas été conçu pour fonctionner ainsi dès le départ ? Car j'ai mis pas mal de temps à écrire les scripts que j'utilise actuellement. Je vais quand-même chercher s'il n'existe pas une astuce qui permettrait un fonctionnement acceptable de l'ensemble des scripts tel que je le souhaite, sans que ce soit trop une usine à gaz et même si ça ne respecte pas le contexte "multi-thread" normalement prévu pour ce type de situation. Si je trouve quelque chose dans quelques jours, je proposerai une solution. En attendant, encore merci pour ta contribution. Krys006 |
|
|
00
|
|
|
#6 |
![]() ![]() ![]() Inscription : avril 2004 Messages : 13 530 ![]() |
Avec Win32::Process, tu peux parfaitement faire ce que tu veux.
Lit bien la documentation car il te permet d'obtenir pleins d'informations. Moi, sans avoir à faire du multithread et company, je ferais ceci. ton script 1 se lance. En début de script 1, tu génères un fichier qui contiendra le début (date) d'execution et la pid. Ton script 2 se lance et lit ce fichier. S'il est présent => le script 1 tourne et tu compares les dates. si besoin, tu kill le process via l'information contenu dans le fichier. si le fichier n'existe plus. Tu considères qu'il est fini. Ou mieux, tu ne fais qu'un seul programme qui lance le programme 1 via Win32::Process. Ainsi, tu auras toutes les informations possible (pid, date de début...). Ensuite, tu te fais une boucle qui endort ton programme pendant 1 minute par exemple. Au bout de 60 boucles, si tu estimes que le programme 1 est trop long, tu le kill toujours via Win32::Process et roule ma poule. Ce module est excellent et je l'utilise régulièrement lorsque je fais des interface Tk vu que le multithread n'y est pas natif. Il te permet de lancer des programmes externes dans un autre process et tu as donc la main dans ton programme. Voilà !
__________________
|
|
|
00
|
|
|
#7 | ||||||
|
Invité régulier
![]() Inscription : novembre 2007 Messages : 49 ![]() |
Merci pour cette idée. J'étais justement en train d'écrire quelque chose dans le même genre, mais avec 2 fichiers.
Si je reprends ton raisonnement, il y a juste un point que je ne comprends pas bien. Le script 1 génère un fichier en début d'exécution (disons script_1.log) contenant l'heure. Ensuite, le script 2 se lance et regarde si le fichier script_1.log existe, d'accord, mais pour en conclure que cela signifie que le script_1 tourne toujours, il faut que ce fichier soit supprimé à la fin du script_1, il me semble ? Avant de creuser le module Win32 :: Process qui a l'air d'être l'outil qu'il me faut, voilà ma version qui semble fonctionner avec 2 fichiers, un stockant le début d'exécution et l'autre l'heure d'exécution juste avant de se terminer, mais utilisant cependant les tâches programmées de Windows pour le script qui contrôle les autres. C'est peut-être basique, mais pour les personnes qui souhaitent réaliser ce genre d’interaction entre plusieurs scripts, ça permet de trouver une solution temporaire à moindre frais, en attendant d'utiliser le multi-process. Voilà le fonctionnement en détails : TEST en fonctionnement "manuel" 1- Le script_1 se lance et dès le début crée le fichier DEBUT.txt dans lequel on écrit l'heure (plus la date si nécessaire) et le PID du script_1 grâce à la variable $$. Ce script_1 fait ensuite semblant de travailler 60s avec un sleep 60; Puis, avant de se terminer, on crée le fichier FIN.txt dans lequel on écrit l'heure de fin et éventuellement le PID. 2- Lancement du script_gen (qui contrôle les 2 autres) A) On lance le script_gen 30 s après, donc avant la fin du script_1 On teste si le fichier FIN.txt existe, comme ce n'est pas le cas, si on considère qu'il devrait être terminé, on arrête le script_1 grâce à la commande kill et au PID que l'on peut lire dans le fichier DEBUT.txt. kill(9,$pid1); Puis on relance le script_1 avec la commande system. B) On lance le script_gen plus de 60s après. Comme le fichier FIN.txt a été crée, c'est suffisant pour conclure que le script_1 a bien été exécuté (même si on peut faire des vérification plus précises). On peut alors lancer le script_2. TEST en fonctionnement "auto" Si l'on veut que ça fonctionne tout seul, sans le module gérant le multi-process, on utilise les tâches programmées ainsi. Supposons que en temps normal, le script_1 fonctionne moins de 5 min, alors : 1- Tâche programmée : 12h00 - Lancement du script_1 2- Tâche programmée : 12h05 - Lancement du script_gen A) S'il trouve le fichier FIN.txt, le script_1 est terminé et on lance le script_2. Le script_gen s'arrête. B) S'il ne trouve pas le fichier FIN.txt, le script_1 tourne toujours, on l'arrête et on le relance. Mais, pour se trouver à un moment dans la configuration où le fichier FIN.txt existe, il faut que le script_gen soit exécuté à nouveau? C'est pourquoi dans la tâche programmée concernant le script_gen, il faut choisir de l'exécuter toutes les 5 min ou plus pour pouvoir laisser au script_1 le temps de s'exécuter correctement une fois relancé. On peut aussi choisir comme option une heure butoir après laquelle le script_gen ne s'exécute plus, mais dans ce cas on n'aura pas la garantie que le script_1 aura bien fait son travail. Après, on peut peaufiner. Stocker le nombre de tentatives, et au bout de N essais déclencher une alerte, envoyer un mail, etc. C'est sans doute un peu tiré par les cheveux et sûrement moins souple et performant que l'utilisation de Win32 :: Process que je vais m'empresser de mettre en oeuvre rapidement, mais ça peut être une solution temporaire si l'on ne peut s'investir davantage à un moment donné. Une dernière question : En attendant qu'un script qui ne fait rien se termine, je souhaite afficher un décompte. Pourquoi ce qui suit ne marche pas : for(my $i=0; $i<=10; $i++) { print "$i "; sleep 1; } En règle générale, je rencontre souvent des problèmes lorsque j'insère la commande sleep dans des boucles. Ci-dessous le code des scripts. Krys006 Code de script_gen.pl Code :
Code :
Code :
|
||||||
|
|
00
|
|
|
#8 | |
|
Membre Expert
![]() Laurent R.Conseil - Consultant en systèmes d'information Inscription : mai 2012 Messages : 570 ![]() |
Citation:
Essaie ceci: Code :
for(my $i=0; $i<=10; $i++) { print "$i \n "; sleep 1; }
__________________
Sauf mention contraire explicite, les bouts de code que je poste en réponse à une question n'ont pas forcément été testés. |
|
|
|
00
|
|
|
#9 |
|
Invité régulier
![]() Inscription : novembre 2007 Messages : 49 ![]() |
OK, je comprends mieux !
Merci |
|
|
00
|
|
|
#10 | ||||||
![]() ![]() ![]() Inscription : avril 2004 Messages : 13 530 ![]() |
Voici une solution avec le module Win32.
Script main.pl qui lance le script 1 et 2 Code :
Code :
Code :
Dans le programme main.pl, tu as les variables $repertoire_progs et $LIMITATION à modifier pour le tester.
__________________
|
||||||
|
|
10
|
|
|
#11 | ||||||
|
Invité régulier
![]() Inscription : novembre 2007 Messages : 49 ![]() |
Merci Djibril, c'est super.
Pendant que tu écrivais cet exemple, de mon côté j'ai creusé un peu le fonctionnement de Win32 :: Process et regarder du coté de la FAQ. J'ai écrit quelque chose censé représenter la situation de mes scripts. Du coup, en voyant ton code, le mien me paraît simpliste, mais il semble marcher. Je décris comment fonctionne mon exemple et ensuite je pose quelques questions : Dans mon script de contrôle (l'équivalent de ton main.pl), je crée le processus 1 qui lance le script 1 (représentant le script de téléchargement) en lui passant en argument le temps d'exécution en sec. Ce script 1 doit obligatoirement être terminé avant de lancer le script 2. Mais s'il met trop de temps, je doit l'arrêter et le relancer. Je stocke l'heure de début en secondes (depuis 1970...) dans une variable. Puis je boucle toutes les secondes pour savoir si le processus 1 est terminé, en basant mon test sur la valeur de $exitcode (c'est peut-être là qu'est la mauvaise idée ?). Si le processus 1 est terminé, c'est bon, je passe au processus 2 qui lance le script 2. Si la durée du processus 1 dépasse le délai de fonctionnement, je l'arrête et le relance, mais en passant une autre durée qui se trouve dans le tableau @durees_p1, je réinitialise l'heure de début et j'incrémente le compteur de scénario. On attend 1 seconde, et on recommence. Quand je teste, ça à l'air de marcher. Maintenant, est-ce que c'est la bonne manière de procéder pour savoir si un process est terminé, alors que toi tu te procures la liste de tous les processus avec le module Win32 :: Process :: Info, puis tu regardes si le pid du processus 1 est dans cette liste. Dans les 3 premières exécutions correspondant à 15, 12 et 13 s, il y a interruption du processus 1 et nouvelle exécution. Ce que je trouve bizarre, c'est que lorsqu'on lance manuellement un script et que l'on affiche son pid, à chaque nouveau lancement celui-ci change. Or, dans mon exemple, c'est toujours le même associé à $ProcessObj_1. Je me demande si dans le cas d'un nombre d'appels important dans le cadre d'une boucle comme celle-ci, il n'y a pas une utilisation croissante de la mémoire qui m'échappe et qui pourrait faire planter le script de contrôle au bout d'un certain nombre d'appels. C'est cette partie qui me pose problème, et la manière de bien écrire le code. Dans ton code, ta boucle sert à s'assurer que le script 1 ne dépasse pas un certain temps d'exécution, auquel cas on le stoppe sans le relancer et on lance le script 2. En résumé : Comment s'y prendre correctement pour relancer le même processus dans une boucle ? Le script Multi_process_script_cont.pl Code :
Code :
Code :
|
||||||
|
|
00
|
|
|
#12 |
|
Membre Expert
![]() Laurent R.Conseil - Consultant en systèmes d'information Inscription : mai 2012 Messages : 570 ![]() |
Djibril a juste pas bien interprété l'une de tes contraintes.
Si tu veux relancer le script 1 si tu as dû le flinguer, tu changes le code qu'il a proposé pour relancer le premier script. J'ai cru comprendre que tu voulais dans ce cas lui donner un peu plus de temps avant de le flinguer. Une technique fréquemment employée pour déterminer le nouveau délai de time out dans ce genre de cas est de suivre une suite de Fibonacci (également connu sous le nom de Léanard de Pise): Code :
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, etc. Cette suite a la propriété que le rapport entre deux termes successifs converge (assez rapidement) vers le nombre d'or (ou la divine proportion): (racine (5) + 1) / 2, soit 1,618033989). Pour prendre un exemple, 55/34 = 1,6176, ce qui est assez proche de la section d'or. Je ne vois aucune raison théorique pour laquelle cette suite devrait marcher mieux qu'une autre, mais il semble qu'elle donne souvent de bons résultats pour ce genre de problème. Plusieurs module du CPAN utilisent cette suite pour trouver des intervalles progressifs de time out.
__________________
Sauf mention contraire explicite, les bouts de code que je poste en réponse à une question n'ont pas forcément été testés. |
|
|
00
|
|
|
#13 |
|
Invité régulier
![]() Inscription : novembre 2007 Messages : 49 ![]() |
Merci pour cette info technique, c'est toujours bon à savoir.
En fait, si je change le temps d'exécution du script 1, c'est pour simuler des variations dans le temps d'exécution de mon script de téléchargement, et voir si le programme boucle bien jusqu'à ce que le script fonctionne pendant un temps inférieur à temps_max. Mais en ce qui concerne le vrai script, je ne lui passe rien, je sais seulement qu'en temps normal il se termine au bout de 5-6 min. Une autre question que j'ai oublié de poser : je ne comprends pas bien le but de l'instruction $ProcessObj->Wait($timeout), en particulier le cas $ProcessObj->Wait(INFINITE). |
|
|
00
|
|
|
#14 | |||
|
Invité régulier
![]() Inscription : novembre 2007 Messages : 49 ![]() |
J'ai trouvé mon erreur !
Citation:
Donc ça marche ainsi (code du if uniquement) : Code :
Dans la boucle, après avoir tué le processus, ne faut-il pas utiliser une instruction du genre $ProcessObj_1->destroy avant de créer un nouveau processus ? |
|||
|
|
00
|
|
|
#15 |
![]() ![]() ![]() Inscription : avril 2004 Messages : 13 530 ![]() |
Pas besoin, la méthode kill du module tue le processus correctement. Tu peux t'en rendre compte en ouvrant le gestionnaire des taches pendant l'exécution de ton programme.
__________________
|
|
|
00
|
|
|
#16 |
|
Invité régulier
![]() Inscription : novembre 2007 Messages : 49 ![]() |
Ok, je commence à voir comment tout ça se met en place.
Merci de m'avoir mis le pied à l'étrier ! Krys006 |
|
|
00
|
Copyright © 2000-2013 - www.developpez.com