2008-12-13 10 views
22

J'ai un script Perl qui doit exécuter un autre script Perl. Ce deuxième script peut être exécuté directement sur la ligne de commande, mais je dois l'exécuter depuis mon premier programme. Je vais devoir lui passer quelques paramètres qui seraient normalement passés quand il est exécuté de manière autonome (le premier script s'exécute périodiquement, et exécute le second script sous un certain ensemble de conditions du système).Comment exécuter un script Perl depuis un script Perl?

Les recherches préliminaires de Google suggèrent d'utiliser des retours arrière ou un appel system(). Y a-t-il d'autres façons de l'exécuter? (Je suppose que oui, puisque c'est Perl dont nous parlons: P) Quelle méthode est préférée si j'ai besoin de capturer la sortie du programme invoqué (et, si possible, rediriger cette sortie comme elle s'exécute sur stdout comme si la seconde programme ont été invoqués directement)?

(Edit:.. Oh, maintenant suggère SO quelques questions connexes This one est proche, mais pas exactement la même chose que ce que je demande au deuxième programme prendra probablement une heure ou plus à courir (beaucoup de I/O), donc je ne suis pas sûr qu'une invocation unique soit la bonne solution pour cela.)

Répondre

28

Vous trouverez l'emplacement de votre interpréteur perl actuel dans la variable spéciale $^X. Ceci est important si perl n'est pas dans votre chemin, ou si vous avez plusieurs versions de Perl disponibles, mais pour vous assurer que vous utilisez le même dans tous les domaines. Lors de l'exécution de commandes externes, y compris d'autres programmes Perl, la détermination de leur exécution peut être assez difficile. Inspectant $? peut laisser des cicatrices mentales durables, donc je préfère utiliser IPC::System::Simple (disponible à partir du CPAN):

use strict; 
use warnings; 
use IPC::System::Simple qw(system capture); 

# Run a command, wait until it finishes, and make sure it works. 
# Output from this program goes directly to STDOUT, and it can take input 
# from your STDIN if required. 
system($^X, "yourscript.pl", @ARGS); 

# Run a command, wait until it finishes, and make sure it works. 
# The output of this command is captured into $results. 
my $results = capture($^X, "yourscript.pl", @ARGS); 

Dans les deux exemples ci-dessus tous les arguments que vous souhaitez passer à votre programme externe vont dans @ARGS. Le shell est également évité dans les deux exemples ci-dessus, ce qui vous donne un petit avantage de vitesse, et évite toute interaction indésirable impliquant des méta-caractères shell. Le code ci-dessus s'attend également à ce que votre deuxième programme renvoie une valeur de sortie nulle pour indiquer le succès; si ce n'est pas le cas, vous pouvez spécifier un premier argument supplémentaire des valeurs de sortie admissibles:

# Both of these commands allow an exit value of 0, 1 or 2 to be considered 
# a successful execution of the command. 

system([0,1,2], $^X, "yourscript.pl", @ARGS); 
# OR 
capture([0,1,2, $^X, "yourscript.pl", @ARGS); 

Si vous avez un processus de longue durée et que vous voulez traiter ses données en il est généré, alors vous Vous aurez probablement besoin d'un tuyau ouvert ou d'un des modules IPC les plus lourds du CPAN. Cela dit, chaque fois que vous avez besoin d'appeler un autre programme Perl de Perl, vous pouvez envisager d'utiliser un module comme meilleur choix. Le démarrage d'un autre programme comporte de nombreux frais généraux, à la fois en termes de coûts de démarrage et de coûts d'E/S pour le transfert de données entre les processus. Cela augmente également considérablement la difficulté de traitement des erreurs. Si vous pouvez transformer votre programme externe en module, vous trouverez peut-être qu'il simplifie votre conception globale.

Tous les meilleurs,

Paul

6

Utilisez les guillemets si vous souhaitez capturer la sortie de la commande. Utilisez system si vous n'avez pas besoin de capturer la sortie de la commande.

TMTOWTDI: il y a donc d'autres façons, mais ce sont les deux plus faciles et les plus probables.

1
#!/usr/bin/perl 
use strict; 

open(OUTPUT, "date|") or die "Failed to create process: $!\n"; 

while (<OUTPUT>) 
{ 
    print; 
} 

close(OUTPUT); 

print "Process exited with value " . ($? >> 8) . "\n"; 

Cela va démarrer le processus date et redirigez la sortie de la commande du DESCRIPTEUR de sortie qui vous permet de traiter une ligne à la fois. Lorsque la commande est terminée, vous pouvez fermer le handle de fichier de sortie et récupérer la valeur de retour du processus. Remplacez date par ce que vous voulez.

3

Si vous avez besoin d'appeler votre script externe de manière asynchrone -Tu veux juste lancer et de ne pas attendre à Terminer-, puis:

# On Unix systems, either of these will execute and just carry-on 
# You can't collect output that way 
`myscript.pl &`; 
system ('myscript.pl &');  

# On Windows systems the equivalent would be 
`start myscript.pl`; 
system ('start myscript.pl'); 

# If you just want to execute another script and terminate the current one 
exec ('myscript.pl'); 
9

je peux penser à quelques façons de le faire. Vous avez déjà mentionné les deux premiers, je ne vais donc pas entrer dans les détails.

  1. backticks: $ retVal = `` perl certainsPerlScript.pl ;
  2. appeler
  3. eval

system()

  • Le eval peut être accompli par siphonage l'autre fichier dans une chaîne (ou une liste de chaînes), puis « eval'ing les cordes. Voici un échantillon:

    #!/usr/bin/perl 
    open PERLFILE, "<somePerlScript.pl"; 
    undef $/; # this allows me to slurp the file, ignoring newlines 
    my $program = <PERLFILE>; 
    eval $program; 
    

    4. faire:

    do 'somePerlScript.pl'

  • +0

    Ne serait-il pas beaucoup plus facile et plus concis d'utiliser simplement 'faire'? – innaM

    +0

    Merci. J'ai ajouté "faire" à la liste. – Colin

    4

    Voir la documentation perlipc pour plusieurs options de communication interprocessus.

    Si votre premier script ne fait que configurer l'environnement du second script, vous recherchez peut-être exec.

    9

    Vous avez déjà obtenu de bonnes réponses à votre question, mais il y a toujours la possibilité d'adopter un point de vue différent: vous devriez peut-être envisager de refactoriser le script que vous voulez exécuter depuis le premier script.Transformez la fonctionnalité en module. Utilisez le module du premier et du deuxième script.

    +0

    J'ai réfléchi à cela il y a un moment, mais je l'ai annulé pour deux raisons. Premièrement, un autre développeur travaille sur le second programme, et deuxièmement, le second a été terminé il y a des mois, et ce premier script pour l'automatiser était juste pensé. Si je recommençais, je ferais probablement des modules à la place. – cbowns

    23

    Vous pouvez juste faire il. Empêche la surcharge du chargement dans une autre copie de Perl.

    0

    Je voulais faire quelque chose comme ceci pour décharger les non-sous-routines dans un fichier externe pour faciliter la modification. En fait, j'ai fait cela dans un sous-programme. L'avantage de cette façon est que ces "mes" variables dans le fichier externe sont déclarées dans l'espace de noms principal. Si vous utilisez 'do', ils ne migreront apparemment pas vers l'espace de noms principal. Notez que la présentation ci-dessous n'inclut pas le traitement des erreurs

    sub getcode($) { 
        my @list; 
        my $filename = shift; 
        open (INFILE, "< $filename"); 
        @list = <INFILE>; 
        close (INFILE); 
        return \@list; 
    } 
    
    # and to use it: 
    
    my $codelist = []; 
    $codelist = getcode('sourcefile.pl'); 
    eval join ("", @$codelist); 
    
    Questions connexes