Comment puis-je 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 transmettre quelques paramètres qui seraient normalement transmis lorsqu'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 système).
Les recherches préliminaires sur Google suggèrent d'utiliser des backticks ou un appel system (). Il n'existe aucun 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 lorsqu'elle s'exécute vers stdout comme si le deuxième programme était invoqué directement)?
(Edit: ah, maintenant suggère des questions connexes. celui-ci {[8] } est proche, mais pas exactement la même chose que ce que je demande. Le deuxième programme prendra probablement une heure ou plus à exécuter (beaucoup d'E / S), donc Je ne suis pas sûr qu'une invocation unique soit la bonne solution pour cela.)
9 réponses
L'emplacement de votre interpréteur perl actuel se trouve 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 la même dans tous les domaines.
Lors de l'exécution de commandes externes, y compris D'autres programmes Perl, déterminer si elles sont réellement exécutées peut être assez difficile. Inspecter $?
peut laisser des cicatrices mentales durables, donc je préfère utiliser IPC:: System:: Simple (disponible auprès 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 transmettre à votre programme externe d'aller 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 zéro pour indiquer le succès; si ce n'est pas le cas, vous pouvez spécifier un premier argument supplémentaire de admissible valeurs de sortie:
# 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 pendant que Il est généré, alors vous aurez probablement besoin d'un canalisé ouvert, ou l'un des modules IPC les plus lourds du CPAN.
Après avoir dit tout cela, chaque fois que vous devez appeler un autre programme Perl de Perl, vous pouvez envisager si l'utilisation d'un module serait un meilleur choix. Démarrer un autre programme comporte quelques frais généraux, à la fois en termes de démarrage coûts, et les coûts d'E / S pour le déplacement des données entre les processus. Cela augmente également considérablement la difficulté de la gestion des erreurs. Si vous pouvez transformer votre programme externe en un module, vous trouverez peut-être qu'il simplifie votre conception globale.
Tout le meilleur,
Paul
Vous pouvez simplement le faire.
{
local @ARGV = qw<param1 param2 param3>;
do '/home/buddy/myscript.pl';
}
Empêche la surcharge de chargement dans une autre copie de perl.
Vous avez déjà de bonnes réponses à votre question, mais il y a toujours la possibilité de prendre un point de vue différent: peut-être devriez-vous envisager de refactorer le script que vous voulez exécuter à partir du premier script. Transformez la fonctionnalité en module. Utilisez le module du premier et du deuxième script.
Je peux penser à quelques façons de le faire. Vous avez déjà mentionné les deux premiers, donc je ne vais pas entrer dans les détails sur eux.
- backticks: $ retVal = " perl somePerlScript.pl
;
- appel système ()
- eval
L'évaluation peut être effectuée en divisant l'autre fichier en une chaîne (ou une liste de chaînes), puis en "eval" 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:
do 'somePerlScript.pl'
Utiliser backticks si vous avez besoin de capturer la sortie de la commande.
Utiliser system
si vous n'avez pas besoin de capturer la sortie de la commande.
TMTOWTDI: il y a donc d'autres moyens, mais ce sont les deux plus faciles et les plus probables.
Si vous avez besoin d'appeler de manière asynchrone votre script externe - vous voulez juste 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');
Voir la documentation perlipc pour plusieurs options de communication interprocess.
Si votre premier script configure simplement l'environnement pour le second script, vous recherchez peut-être exec
.
#!/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 canaliser la sortie de la commande vers la sortie filehandle que vous pouvez traiter une ligne à la fois. Lorsque la commande est terminée, vous pouvez fermer le filehandle de sortie et récupérer la valeur de retour du processus. Remplacez date
par ce que vous voulez.
Je voulais faire quelque chose comme ça pour décharger les non-sous-Programmes dans un fichier externe pour faciliter l'édition. J'ai effectivement fait une sous-routine. L'avantage de cette façon est que ces variables" Mes " dans le 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. Remarque 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);