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

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

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

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

Ответ 1

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

Все самое лучшее,

Пол

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

  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. делать:

do 'somePerlScript.pl'

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

#!/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 тем, что вы хотите.

Ответ 9

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