【问题标题】:How do I run a Perl script from within a Perl script?如何从 Perl 脚本中运行 Perl 脚本?
【发布时间】:2010-09-26 18:18:22
【问题描述】:

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

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

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

【问题讨论】:

    标签: perl ipc


    【解决方案1】:

    我可以想出几种方法来做到这一点。前两个你已经提到了,我就不详细说了。

    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'

    【讨论】:

    • 简单地使用'do'不是更简单更简洁吗?
    • 好名单。你能详细说明不同之处吗?例如,用do FILE 评估的代码看不到封闭范围内的词法; eval STRING 确实如此。等等。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      你可以它。

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

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

      【讨论】:

        【解决方案4】:

        您当前的 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 );
        

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

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

        一切顺利,

        保罗

        【讨论】:

          【解决方案5】:

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

          【讨论】:

          • 我不久前考虑过,但出于两个原因将其取消。首先,另一位开发人员正在开发第二个程序,其次,第二个程序是几个月前完成的,而第一个自动化它的脚本是刚刚想到的。如果我再做一次,我可能会改用模块。
          【解决方案6】:

          请参阅perlipc 文档了解进程间通信的几个选项。

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

          【讨论】:

            【解决方案7】:

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

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

            【讨论】:

              【解决方案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】:

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

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

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

                【讨论】:

                  最近更新 更多