从perl子程序返回错误消息

16

这是从Perl子程序返回错误消息的好方法吗?

sub some_subroutine{
    # do something
    $something = 14;

    if(1 == 2){
        $_ = "This should not be happening!";
        return undef;
    }
    return $something;
}

my $ret=some_subroutine();
print "ERROR: $_" unless(defined $ret);

这段代码在一个并行世界中可以正常运行(在那里1 == 2),但使用$_来返回错误信息是一个好的方式吗?我没有找到关于在这种情况下使用$_的任何文档。

谢谢!

5个回答

23

$_不是一个好的机制,因为许多其他元素使用并设置它。

你可以设置一些其他的全局变量,例如$Error,但最好的方法是抛出异常。 这样用户就不必总是检查和忘记它,它只是发生了。在Perl中,异常是用"die"生成的。die可以接受一个字符串或一个对象。有许多模块可以使抛出异常变得更容易,包括Exception::ClassException::SimpleOuchautodie

你可以用eval {}捕获异常,但这存在许多问题,所以你应该使用类似Try::Tiny的东西。


当错误不是“异常”时,返回一些错误/未定义状态是否在语义上有价值?如果您期望某个文件可能不存在,从语义上讲,返回错误状态而不是抛出异常是否更有意义?我知道这只是一个小问题... - wprl
3
不要过于纠结“exception”这个词的精确含义。如果子程序无法完成其工作,则属于异常情况。如果它意味着调用者不能期望正常的返回值,那也是一个异常情况。如果你编写一个检查文件存在性的子程序,如果文件不存在就不是异常情况。但如果你编写一个读取和解析文件的程序,如果文件不存在,它应该抛出一个异常。 - Schwern
4
如果整个系统不需要那个文件也能正常运行,你可以在外部加一个检查文件是否存在的包装器,如果不存在则返回一个空结构。最后,程序员可能会忘记编写错误处理代码,但你必须有意地忽略异常。这是为了懒惰而优化的。 - Schwern

8

将错误信息存在全局变量中并不罕见。例如,内置函数使用$!,而DBI使用$DBI::errstr。但是这样就会继承使用全局变量的所有问题。信号处理程序需要对它们进行本地化,析构函数可能会破坏它们,还有多线程问题等。

在我看来,抛出异常(例如使用die)是更常见的选择。

无论你做什么,都不要使用$_。它经常用作别名,因此使用它可能会产生意想不到的后果。


3
你可以做的一件事是,在出现错误/未定义结果时简单地return;。在某些情况下,return undef;可能会被评估为true。你可以查看Perl Best Practices中关于错误处理的章节,它涵盖了这个问题并提供了其他有用的指导。
例如,如果:
my $ret=some_subroutine();
print "ERROR: $_" unless(defined $ret);

变成

my @ret=some_subroutine();         # oops!
print "ERROR: $_" unless(defined $ret);

你遇到了一个奇怪的错误(不能捕获错误),很难追踪,但是如果你从some_subroutine中使用return;,你会得到:

my @ret=some_subroutine();         # oops!
print "ERROR: $_" unless($ret);    # but error is still caught

此外,对于应该返回列表的函数:
my @ret=some_other_subroutine();    # OK
print "ERROR: $_" unless($ret);     # error will be caught if you "return;"

# ... but later ...

my $ret=some_other_subroutine();    # oops!
print "ERROR: $_" unless($ret);     # if you "return;" $ret will equal 0
                                    # (the length of the returned list)
                                    # and the error will be caught

因此,虽然有其他返回函数错误/未定义状态的模式,但始终使用return;允许在几乎所有函数中使用一个模式,无论是返回适当的值还是在检查错误状态时。

4
这是PBP中错误建议的一个例子。如果子程序通常返回标量,则在出错时返回undef,而不是空列表。 - ysth
这很有趣,但你还没有说服我。如果在列表上下文中调用函数,则返回一个单元素列表(undef)似乎同样糟糕。子例程可能会返回标量,但是如果您从列表上下文中调用它,则不会得到标量(如果我理解正确的话)。当您return;时,您可以始终确保!foo()为真,但如果您return undef;则不能。 - wprl
https://dev59.com/r3A75IYBdhLWcg3wK1wp - wprl
如果一个子程序通常返回一个列表,那么是的,返回一个空列表。return undef;@x=somesub())的典型错误情况可能只会在子程序通常用于列表上下文时遇到;return;%x = ( 'sub1' => sub1(), 'sub2' => sub2() ))的典型错误情况可能只会在子程序通常用于标量上下文时遇到。 - ysth
1
也许明确的情况会有所帮助?sub show_bar { my%args = @_; print“bar:$ args {'bar'}\ n”} sub get_foo {return;} show_bar('foo'=> get_foo(),'bar'=>'barbar')在这里,通过名称和用法,get_foo显然应该返回一个标量,但在错误情况下它没有,导致不仅出现错误,而且无法直接追溯到get_foo调用。这经常发生在到处使用return;的人身上。 - ysth
显示剩余10条评论

1

0
对于一个 Web 服务,你可以编写一个子程序来打印自定义错误响应(40x 错误等)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接