Как запустить скрипт Perl из скрипта Perl? - PullRequest
24 голосов
/ 13 декабря 2008

У меня есть скрипт Perl, который должен выполнить другой скрипт Perl. Этот второй сценарий может быть выполнен непосредственно из командной строки, но мне нужно выполнить его из моей первой программы. Мне нужно будет передать ему несколько параметров, которые обычно передаются, когда он запускается автономно (первый скрипт запускается периодически и выполняет второй скрипт при определенном наборе системных условий).

Предварительный поиск в Google предлагает использовать обратные ссылки или системный вызов (). Есть ли другие способы запустить его? (Я предполагаю, что да, поскольку мы говорим о Perl: P) Какой метод предпочтителен, если мне нужно захватить вывод из вызванной программы (и, если возможно, передать этот вывод при выполнении в stdout, как будто второй программа была вызвана напрямую)?

(Изменить: о, сейчас SO предлагает некоторые связанные вопросы. Этот близок, но не совсем так, как я спрашиваю. Вторая программа, скорее всего, займет час или более (много операций ввода-вывода), поэтому я не уверен, что одноразовый вызов подходит для этого.)

Ответы [ 9 ]

32 голосов
/ 15 декабря 2008

Вы можете просто сделать это.

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

Предотвращает накладные расходы на загрузку в другой копии Perl.

30 голосов
/ 13 декабря 2008

Местоположение вашего текущего интерпретатора Perl можно найти в специальной переменной $^X. Это важно, если perl не находится на вашем пути, или если у вас есть несколько доступных версий perl, но чтобы убедиться, что вы используете одну и ту же версию по всем направлениям.

При выполнении внешних команд, включая другие программы Perl, определить, действительно ли они выполнялись, может быть довольно сложно. Проверка $? может оставить стойкие психические шрамы, поэтому я предпочитаю использовать IPC :: System :: Simple (доступно из 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);

В обоих приведенных выше примерах любые аргументы, которые вы хотите передать своей внешней программе, входят в @ARGS. Оболочку также избегают в обоих приведенных выше примерах, что дает небольшое преимущество в скорости и позволяет избежать нежелательных взаимодействий, связанных с метасимволами оболочки. Приведенный выше код также ожидает, что ваша вторая программа вернет нулевое значение выхода, чтобы указать успех; если это не так, вы можете указать дополнительный первый аргумент допустимых значений выхода:

 # 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 );

Если у вас длительный процесс и вы хотите обработать его данные , пока он генерируется, то вам, вероятно, понадобится открытый канал или один из более тяжелых модулей IPC из CPAN.

Сказав все это, каждый раз, когда вам нужно будет вызывать другую Perl-программу из Perl, вы можете подумать, будет ли лучше использовать модуль. Запуск другой программы влечет за собой немало накладных расходов, как с точки зрения затрат на запуск, так и затрат на ввод / вывод для перемещения данных между процессами. Это также значительно увеличивает сложность обработки ошибок. Если вы можете превратить вашу внешнюю программу в модуль, вы можете обнаружить, что это упрощает ваш общий дизайн.

Всего наилучшего,

Пол

12 голосов
/ 13 декабря 2008

У вас уже есть хорошие ответы на ваш вопрос, но всегда есть возможность принять другую точку зрения: возможно, вам следует подумать о рефакторинге сценария, который вы хотите запустить из первого сценария. Преврати функциональность в модуль. Используйте модуль из первого и второго скрипта.

11 голосов
/ 13 декабря 2008

Я могу придумать несколько способов сделать это. Вы уже упомянули первые два, поэтому я не буду вдаваться в подробности о них.

  1. обратные пометки: $ retVal = `` perl somePerlScript.pl ;
  2. системный () вызов
  3. Eval

Оценка может быть достигнута, превращая другой файл в строку (или список строк), а затем 'оценивая' строки. Вот образец:

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

4. делать:

do 'somePerlScript.pl'
6 голосов
/ 13 декабря 2008

Если вам нужно асинхронно вызвать ваш внешний скрипт - вы просто хотите запустить его, а не ждать его завершения -, тогда:

# 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 голосов
/ 13 декабря 2008

Используйте обратные метки, если вам нужно захватить выходные данные команды.

Используйте system, если вам не нужно фиксировать вывод команды.

TMTOWTDI: так что есть и другие способы, но это два наиболее простых и вероятных способа.

5 голосов
/ 13 декабря 2008

См. Документацию perlipc для нескольких вариантов межпроцессного взаимодействия.

Если ваш первый сценарий просто устанавливает среду для второго сценария, возможно, вы ищете exec.

1 голос
/ 13 декабря 2008
#!/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";

Это запустит процесс date и перенаправит вывод команды в файловый дескриптор OUTPUT, который вы можете обрабатывать одновременно. Когда команда завершена, вы можете закрыть выходной дескриптор файла и получить возвращаемое значение процесса. Замените date на то, что вы хотите.

0 голосов
/ 05 февраля 2013

Я хотел сделать что-то подобное, чтобы выгрузить непрограммы во внешний файл, чтобы упростить редактирование. Я фактически превратил это в подпрограмму. Преимущество этого способа заключается в том, что эти «мои» переменные во внешнем файле объявляются в основном пространстве имен. Если вы используете «do», они явно не переносятся в основное пространство имен. Обратите внимание, что представленная ниже презентация не включает обработку ошибок

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);
...