【问题标题】:What's the most elegant way to catch a signal in Perl?在 Perl 中捕捉信号最优雅的方法是什么?
【发布时间】:2011-05-28 15:37:03
【问题描述】:

我在 Perl 脚本中有 2 行彼此靠近,可能会抛出 __WARN__。如果第一个抛出,那么我只想从函数中返回而不是尝试继续。

我知道如何在这两行之前设置一个处理程序,以便我可以报告错误等:

local $SIG{__WARN__} = sub {
  my $e = shift;
  # log the error etc.
  return;
};
# possibly warning-resulting line 1
# possibly warning-resulting line 2

但是这发生在两条线上。我宁愿它只是捕获第一个实例并从函数返回。但是该处理程序中的返回只返回处理程序,而不是外部函数。

有没有办法在处理信号时从函数中返回?

【问题讨论】:

  • 您期望什么样的警告?如果您从一开始就知道警告的原因,为什么不在执行第 1 行和第 2 行之前在您的子程序中考虑它?

标签: perl exception signals


【解决方案1】:

将两行代码包装在不同的函数中,并让第一行返回一个状态,指示调用函数应该返回。单独的函数可以根据需要处理警告 - 可能使用相同的函数来执行相同的日志记录。

sub wrap_line_1
{
    local $SIG{__WARN__} = ...;
    ...do line 1...
    return ($warning_fired ? 1 : 0);
}

sub wrap_line_2
{
    local $SIG{__WARN__} = ...;
    ...do line 2...
    return;
}


...calling code...
wrap_line_1() and return;
wrap_line_2();
...

【讨论】:

    【解决方案2】:

    不,您不能强制调用者返回。 (好吧,我相信你可以,用正确的黑魔法 XS 咒语。但不要!)

    这似乎是一个错误的设计;警告不应该是致命错误,当然也不应该这样使用。

    【讨论】:

    • 即使没有黑魔法也可以使用use warnings FATAL => 'all'。有了它,可以相应地处理任何警告抛出和异常。
    • 很公平,虽然我很犹豫是否认为这是“回归”。有关此程序结构的更多信息会有所帮助。
    【解决方案3】:

    您可以在处理程序中使用 die() 并在 eval 中捕获它,如下所示:

    use 5.12.0;
    
    local $SIG{__WARN__} = sub {
      my $e = shift;
      # log the error etc.
      die "$e";
    };
    
    func(undef);
    func('honk');
    
    sub func {
        my $foo = shift;
    
        eval {
           my $bar = "a $foo";
           say "$bar";
        };
        if ($@) {
           die $@ unless $@ =~ /^Use of uninitialized value/;
        }
        return;
    }
    

    不过,您可能想直接检查警告是否会发生并简单地返回:

    sub func {
      my $foo = shift;
      return unless defined($foo);
      ...; # use $foo fine
    }
    

    【讨论】:

      【解决方案4】:

      只需抛出异常并在调用者中捕获并返回。

      让处理程序返回一个值,并在 eval 之后的 if 语句中检查它是否返回。

      实际上,您甚至不必从处理程序返回值,因为它不决定是否返回 - 您总是在捕获时返回。

      具体来说:

      # We're in the caller now
      eval{
          local $SIG{__WARN__} = sub {
              my $e = shift;
              # log the error etc.
              die MyWarnException->new("This is an exception.");
          };
      
          # Offending statements go here
          do_things();
      
      };
      if( $@ && $@->isa('MyWarnException')){
          return;
      }
      

      【讨论】:

      • 那么,您的意思是,让处理程序设置一个全局标志,然后在后续代码中检查是否设置了该标志?
      • 其实我太仓促纠正自己:你确实需要抛出一个异常。让我编辑答案以说明我的意思。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-01
      • 1970-01-01
      • 2010-12-08
      • 1970-01-01
      • 2020-12-04
      相关资源
      最近更新 更多