web-dev-qa-db-fra.com

Comment exécuter un script Perl à partir d'un script Perl?

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 à partir de mon premier programme. Je devrai lui passer quelques paramètres qui devraient normalement être transmis lorsqu'il est exécuté de manière autonome (le premier script s'exécute périodiquement et exécute le deuxième script sous un certain ensemble de conditions système).

Les recherches Google préliminaires suggèrent d'utiliser des backticks ou un appel system (). Existe-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, de diriger cette sortie lors de son exécution vers stdout comme si la seconde programme ont été invoqués directement)?

(Modifier: oh, maintenant SO suggère quelques questions connexes. Celui-ci est proche , mais pas exactement la même chose que ce que je demande. Le deuxième programme prendra probablement une heure ou plus (beaucoup d'E/S), donc je ne suis pas sûr qu'une invocation unique soit la bonne solution pour cela. .)

24
cbowns

L'emplacement de votre interpréteur Perl actuel se trouve dans la variable spéciale $^X. Ceci est important si Perl n'est pas sur votre chemin, ou si vous avez plusieurs versions de Perl disponibles mais pour vous assurer que vous utilisez la même dans tous les domaines.

Lors de l'exécution de commandes externes, y compris d'autres programmes Perl, il peut être assez difficile de déterminer si elles ont réellement été exécutées. Inspection $? peut laisser des cicatrices mentales durables, donc je préfère utiliser IPC :: System :: Simple (disponible sur le 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 transmettre à 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 retourne 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 de valeurs de sortie autorisées:

 # 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 souhaitez traiter ses données pendant que elles sont générées, alors vous allez probablement avoir besoin d'une ouverture de canalisation, ou l'un des modules les plus lourds IPC du CPAN.

Cela dit, chaque fois que vous devez appeler un autre programme Perl à partir de Perl, vous pouvez vous demander si l'utilisation d'un module serait un meilleur choix. Le démarrage d'un autre programme entraîne un certain nombre de 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 gestion des erreurs. Si vous pouvez transformer votre programme externe en module, vous trouverez peut-être que cela simplifie votre conception globale.

Bonne chance,

Paul

30
pjf

Vous pouvez simplement faire cela.

{
    local @ARGV = qw<param1 param2 param3>;
    do '/home/buddy/myscript.pl';
}

Empêche la surcharge de chargement dans une autre copie de Perl.

32
Axeman

Vous avez déjà de bonnes réponses à votre question, mais il est toujours possible de prendre un point de vue différent: vous devriez peut-être envisager de refactoriser le script que vous souhaitez exécuter à partir du premier script. Transformez la fonctionnalité en module. Utilisez le module du premier et du deuxième script.

12
innaM

Je peux penser à quelques façons de procéder. Vous avez déjà mentionné les deux premiers, donc je n'entrerai pas dans les détails.

  1. backticks: $ retVal = `` Perl somePerlScript.pl;
  2. appel system ()
  3. eval

L'évaluation peut être accomplie en transformant l'autre fichier en une chaîne (ou une liste de chaînes), puis en 'évaluant' les chaînes. 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:

faire 'somePerlScript.pl'
11
Colin

Utilisez des raccourcis si vous devez 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 aussi, mais ce sont les deux plus faciles et les plus probables.

6
Jonathan Leffler

Si vous devez appeler de manière asynchrone votre script externe - vous voulez simplement le lancer et ne pas attendre qu'il se termine -, alors:

# 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');
6
Renaud Bompuis

Voir la documentation perlipc pour plusieurs options de communication interprocessus.

Si votre premier script configure simplement l'environnement pour le deuxième script, vous recherchez peut-être exec.

5
brian d foy
#!/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 démarrera le processus date et dirigera la sortie de la commande vers le descripteur de fichier OUTPUT que vous pouvez traiter une ligne à la fois. Une fois la commande terminée, vous pouvez fermer le descripteur de fichier de sortie et récupérer la valeur de retour du processus. Remplacez date par ce que vous voulez.

1
Robert Gamble

Je voulais faire quelque chose comme ça pour décharger les non-sous-programmes dans un fichier externe pour faciliter l'édition. J'ai en fait fait un sous-programme. L'avantage de cette méthode est que ces "mes" variables du fichier externe sont déclarées dans l'espace de noms principal. Si vous utilisez "do", ils ne migrent apparemment pas vers l'espace de noms principal. Notez que la présentation ci-dessous n'inclut pas la gestion 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);
0
Robert