27

我有一个需要执行另一个 Perl 脚本的 Perl 脚本。第二个脚本可以直接在命令行上执行,但我需要在我的第一个程序中执行它。我需要向它传递一些参数,这些参数通常会在它独立运行时传入(第一个脚本定期运行,并在一组特定的系统条件下执行第二个脚本)。

初步的 Google 搜索建议使用反引号或 system() 调用。还有其他方法可以运行吗?(我猜是的,因为我们正在谈论的是 Perl :P)如果我需要从被调用的程序中捕获输出(并且,如果可能的话,当它执行到 stdout 时,就像第二个一样通过管道输出该输出)程序被直接调用)?

(编辑:哦,现在SO 提出了一些相关的问题。这个很接近,但与我要问的不完全相同。第二个程序可能需要一个小时或更长时间才能运行(大量 I/O),所以我不确定一次性调用是否适合此。)

4

9 回答 9

35

你可以做到

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

防止加载另一个 perl 副本的开销。

于 2008-12-15T01:59:10.417 回答
34

您当前的 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. 在上面的两个示例中也避免了 shell,这给您带来了一点速度优势,并避免了涉及 shell 元字符的任何不必要的交互。上面的代码还期望你的第二个程序返回一个零退出值来表示成功;如果不是这种情况,您可以指定允许退出值的附加第一个参数:

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

如果您有一个长时间运行的进程,并且您希望在生成数据对其数据进行处理,那么您可能需要一个管道打开,或者来自 CPAN 的更重量级的 IPC 模块之一。

说了这么多,任何时候你需要从 Perl 调用另一个 Perl 程序,你可能希望考虑使用模块是否是一个更好的选择。启动另一个程序会带来相当多的开销,无论是在启动成本方面,还是在进程之间移动数据的 I/O 成本方面。这也大大增加了错误处理的难度。如果你能把你的外部程序变成一个模块,你会发现它简化了你的整体设计。

祝一切顺利,

保罗

于 2008-12-13T13:21:28.873 回答
13

我可以想到几种方法来做到这一点。你已经提到了前两个,所以我不会详细介绍它们。

  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'

于 2008-12-13T05:40:17.353 回答
12

您的问题已经得到了很好的答案,但总是有可能采取不同的观点:也许您应该考虑重构要从第一个脚本运行的脚本。将功能转换为模块。使用第一个和第二个脚本中的模块。

于 2008-12-13T12:41:40.060 回答
6

如果您需要捕获命令的输出,请使用反引号。

如果system您不需要捕获命令的输出,请使用。

TMTOWTDI:所以还有其他方法,但这是最简单和最有可能的两种。

于 2008-12-13T04:56:29.127 回答
6

如果你需要异步调用你的外部脚本——你只想启动它而不是等待它完成——那么:

# 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');
于 2008-12-13T05:23:07.247 回答
5

有关进程间通信的几个选项,请参阅perlipc文档。

如果您的第一个脚本只是为第二个脚本设置环境,您可能正在寻找exec.

于 2008-12-13T08:06:47.963 回答
1
#!/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用任何你想要的替换。

于 2008-12-13T04:57:50.230 回答
0

我想做这样的事情来将非子程序卸载到外部文件中,以使编辑更容易。我实际上把它变成了一个子程序。这种方式的优点是外部文件中的那些“我的”变量在主命名空间中声明。如果您使用“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);
于 2013-02-05T18:42:46.123 回答