【问题标题】:In Perl is there a way to restart the program currently running from within itself?在 Perl 中有没有办法重新启动当前正在运行的程序?
【发布时间】:2012-09-26 16:52:11
【问题描述】:

我在 Perl 中运行一个程序,该程序在某一时刻评估从子例程中调用的 if 语句中的数据,例如

sub check_good {
    if (!good) {
         # exit this subroutine
         # restart program
    } 
    else {
         # keep going
    }
} # end sub

我遇到的问题是退出和重新启动。我知道我可以直接使用exit 0; 退出,但是如果我想回到开头,显然这是不正确的。我尝试调用基本上启动程序的子程序,但是当然一旦它运行它会再次回到这一点。 我想过把它放在一个while循环中,但这意味着把整个文件放在循环中,这是非常不切实际的。

我实际上不知道这是否可能,所以任何输入都会很棒。

【问题讨论】:

    标签: perl exit subroutine


    【解决方案1】:

    如果您没有更改@ARGV,或者您保留了一份副本,您可能会执行exec($^X, $0, @ARGV) 之类的操作。

    $^X and $0(或$EXECUTABLE_NAME$PROGRAM_NAME,参见下面Brian 的评论)分别是当前的perl 解释器和当前的perl 脚本。

    【讨论】:

    • 原谅我的无知 - 我知道@ARGV 用于命令行参数,但我使用过 所以我可以假设它们将数据存储在相同的内存位置(或其他地方)吗?
    • 这是一个您可以轻松测试的断言。
    • 如果你使用标准输入,那么你将不得不自己缓冲它,也许在一个临时文件中。
    • 我建议使用^X 以确保重新启动的进程使用与原始脚本相同的 perl 解释器。即类似exec( ^X, $0, @ARGV).
    • 为了未来的维护者,我一般推荐使用英文版的“可执行线路噪音”变量,比如exec($EXECUTABLE_NAME, $PROGRAM_NAME, @ARGV)。只需记住use English qw{ -no_match_vars } 或只输入您特别想使用的变量名称以避免性能下降。出于好奇,该命中在 (search.cpan.org/~andk/Devel-SawAmpersand/lib/Devel/…) 中得到了非常简洁的描述,并在 perldoc perlvar 的“与正则表达式相关的变量”部分中提到。
    【解决方案2】:

    另一种方法是始终有两个进程:主管和工作人员。

    将所有逻辑重构为一个名为run(或main 或其他)的子例程。当您的真实逻辑检测到它需要重新启动时,它应该使用预定义的非零退出代码(例如 1)退出。

    那么您的主脚本和主管将如下所示:

    if (my $worker = fork) {
        # child process
        run(@ARGV);
        exit 0;
    }
    # supervisor process
    waitpid $worker;
    my $status = ($? >> 8);
    
    if ($status == 1) { ... restart .. }
    
    exit $status; # propagate exit code...
    

    在您只想重新启动一次的简单场景中,这可能有点矫枉过正。但是,如果您在任何时候都需要能够处理其他错误情况,则此方法可能更可取。

    例如,如果退出代码是 255,这表明主脚本调用了 die()。在这种情况下,您可能需要执行一些决策程序来重新启动脚本、忽略错误或升级问题。

    在 CPAN 上有不少模块实现了此类主管。 Proc::Launcher 就是其中之一,手册页包含对相关作品的广泛讨论。 (我从来没有使用过 Proc::Launcher,主要是因为我链接到了这个讨论)

    【讨论】:

      【解决方案3】:

      没有什么可以阻止你给自己打电话system。像这样的东西(显然需要整理一下),我传入一个命令行参数以防止代码永远调用自己。

      #!/usr/bin/perl
      use strict;
      use warnings;
      
      print "Starting...\n";
      sleep 5;
      
      if (! @ARGV) {
              print "Start myself again...\n";
              system("./sleep.pl secondgo");
              print "...and die now\n";
              exit;
      } elsif ((@ARGV) && $ARGV[0] eq "secondgo") {
              print "Just going to die straightaway this time\n";
              exit;
      }
      

      【讨论】:

      • 此解决方案依赖于预先固定的文件名。当我们有多个链接或符号链接以不同的名称指向该脚本时,这开始很糟糕。这仍然是假设没有执行chdir……此外,您正在创建一个完整的流程级联。调用自己一次并不重要,但是以这种方式重新启动自己 1000 次往往会填满进程表和 RAM。
      • 同意所有这些观点,我只是想表明重新开始自己是可能的。下面和上面的答案显然更全面。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-20
      • 1970-01-01
      • 1970-01-01
      • 2013-02-11
      相关资源
      最近更新 更多