2008-12-13 176 views
22

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

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

(编辑:哦,现在SO提出了一些相关的问题This one接近,但不完全一样,我要问的第二个节目可能会需要一小时或更长时间来运行(大量的我/ O),因此我不确定一次性调用是否适合此操作。)

回答

28

当前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成本来看,启动另一个程序会带来相当多的开销。这也显着增加了错误处理的难度。如果您可以将外部程序转换为模块,则可能会发现它简化了整体设计。

一切顺利,

保罗

6

如果需要捕获命令的输出,请使用反引号。如果您不需要捕获该命令的输出,则使用。 TMTOWTDI:所以也有其他方法,但这些是最简单也是最有可能的。

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和管道命令的输出到输出文件句柄,你可以一次处理一条线。命令完成后,您可以关闭输出文件句柄并检索进程的返回值。用你想要的任何东西替换date

3

如果需要异步调用的外部脚本 - 你只需要启动它,而不是等待它finish-,则:

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

我能想到的几个方法可以做到这一点。你已经提到了前两个,所以我不会详细讨论它们。

  1. 反引号:$ retVal的=``的perl somePerlScript.pl ;
  2. 系统()调用
  3. EVAL

中的eval可以通过啜其他文件转换成字符串(或字符串的列表),然后“eval'ing字符串来实现。继承人样本:

#!/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'

+0

简单地使用'do'会不会更容易和更简洁? – innaM 2008-12-13 12:37:23

+0

谢谢。我在列表中添加了'do'。 – Colin 2008-12-14 20:28:47

4

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

如果您的第一个脚本仅为第二个脚本设置环境,那么您可能正在寻找exec

9

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

23

你可以只它。

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

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

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