Как запустить скрипт Perl из скрипта Perl?

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

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

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

13.12.2008 04:34:18
9 ОТВЕТОВ
РЕШЕНИЕ

Местоположение вашего текущего интерпретатора 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, вы можете подумать, будет ли использование модуля лучшим выбором. Запуск другой программы влечет за собой немало накладных расходов, как с точки зрения затрат на запуск, так и затрат на ввод / вывод для перемещения данных между процессами. Это также значительно увеличивает сложность обработки ошибок. Если вы можете превратить вашу внешнюю программу в модуль, вы можете обнаружить, что это упрощает ваш общий дизайн.

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

Павел

32
13.12.2008 13:27:46

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

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

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

6
13.12.2008 04:56:29
#!/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на что хочешь.

1
13.12.2008 05:10:07

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

# 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.12.2008 05:23:07

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

  1. обратные кавычки: $retVal = `perl somePerlScript.pl`;
  2. system() вызов
  3. eval

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

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

4 делать:

сделать 'somePerlScript.pl'

11
27.09.2019 13:52:40
Разве не было бы намного проще и лаконичнее просто использовать «do»?
innaM 13.12.2008 12:37:23

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

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

5
13.12.2008 08:06:47

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

12
13.12.2008 12:41:40
Я считал это некоторое время назад, но списал это по двум причинам. Во-первых, другой разработчик работает над второй программой, а во-вторых, вторая была закончена несколько месяцев назад, и этот первый скрипт для автоматизации был только что придуман. Если бы я сделал это снова, я бы вместо этого сделал модули.
cbowns 13.12.2008 16:06:33

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

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

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

32
15.12.2008 01:59:10

Я хотел сделать что-то подобное, чтобы перенести непрограммы во внешний файл, чтобы упростить редактирование. Я на самом деле превратил это в подпрограмму. Преимущество этого способа заключается в том, что эти «мои» переменные во внешнем файле объявляются в основном пространстве имен. Если вы используете «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);
0
5.02.2013 18:42:46