在Perl中把数值赋给$!是可行的吗?

6
在Perl中,当出现错误时,将$!分配给变量是否可以?例如:
if( ! (-e $inputfile))
{
      $! = "Input file $inputfile appears to be non-existent\n";
      return undef;
}

这样我就可以在顶层处理所有的错误。
谢谢。

1
我认为Perl根本不会被视为合适的选择... ;) - Jason S
7个回答

13
如果你把值分配给$!,它会被放置在系统的errno变量中,该变量只接受数字。因此,你实际上可以这样做:
use Errno "EEXIST";
$! = EEXIST;
print $!;

您可以获取定义的系统错误号的字符串值,但无法将其设置为任意字符串。这样的字符串会导致“在标量赋值中参数“…”不是数字”的警告,并将errno设置为0。

另一个问题是$!可能会被任何系统调用更改。因此,您只能信任它具有您设置的值,直到进行打印或几乎任何其他操作。您可能需要自己的错误变量。


9

好的,根据文档所说,它是系统错误的指示器。因此,不要将其分配给变量,否则会让你的库的用户感到烦恼。

应该使用异常代替:

eval { # this ain't the evil eval
   # some code
   die $something;
}
if (my $err = $@) {
   # exception handling
}

请注意,您可以使用“throw”或“die”来处理任何需要的内容。

$!中的值不保证是任何东西,除非在一个函数调用之后立即检查它,而该函数a. 失败并b. 声明使用errno。因此,任何感到生气的人都在做错事。 - Chas. Owens
1
只是提醒一下,很多人认为在库中使用“die”是非常不合适的(即非犹太教规定的:))。使用_error/_errors标志和适当的访问器方法(理想情况下,一个用于快速的真/假检查,另一个用于打印错误列表)。最好在对象级别上实现 :) - DVK
@DVK 我非常不同意你的看法。一个库最好抛出异常,只要它们真的是异常情况并且你记录了这是如何处理错误,并且在处理错误方面保持一致。为什么?库的用户会想知道什么时候有错误发生。大多数情况下,如果有错误发生,他们只会让程序崩溃。这会导致到处写"$obj->method or die $obj->error"。你会变得懒惰,忘记并且错过错误。异常意味着你只需要在想从错误中恢复的地方添加特殊代码。使用autodie一段时间,你就会明白。 - Schwern

3
我的拉比说“不!”

7
我想赞同这个回答,但实际上它是合法的,所以回答太错误了,不能点赞 :-). 这里的教训是除非拉比也懂Perl,否则不要相信他的话。 - Nathan Fellman
2
感谢您看到我尝试幽默的愚蠢言论!根据拉比和犹太教的严格定义,将$!分配给它不受犹太教特定法律的保护,因此它不是“合乎规矩的”。但是,对于您这些外邦人来说可能是可以接受的。 - CoderDennis
1
我希望有人能够编辑问题标题,因此在此预先说明原始标题为“在Perl中分配给$!是否合适?” - ysth
胜利者是...有点惊喜。 - innaM
@Dennis,我认为法律未涉及的事情默认都是合法的。例如,没有管辖电脑(除了在安息日使用电),吸烟(同样不包括安息日点火)或者日光浴的法律,但是它们都是合法的。我认为相同的原则适用于分配$! :-) - Nathan Fellman
@Nathan Fellman: 晒太阳可能需要按性别分开以符合宗教要求,即使你认为皮肤癌的危险被允许作为"shomer pasaim hashem"(上帝保护)。 - ysth

3

设定$!是可以的,但是:

  • 你应该在函数结束时进行
  • 你应该返回一个不同的值来指示发生了错误
  • 你应该使用操作系统的errno值而不是字符串进行设置
  • 检查代码需要在函数失败时立即检查该值(仅在函数指示失败时)

需要记住的一点是,die使用$!中的值作为其退出代码(只要它不为零)。


2

是的,您可以将(#'s)分配给$!,但要注意在何处进行分配,以免干扰其他功能的消息。


0

$!有很多注意事项,它是一个全局变量,许多函数都会分配给它(其中一些是Perl调用的C函数),所以我会简单地抛出异常(在Perl中意味着死亡),并让用户捕获它,如果他们关心的话。因此,不要写成:

$obj->foo or die $!;
$obj->bar or die $!;
$obj->baz or die $!;

或者甚至更好

$obj->foo or die $obj->error;
$obj->bar or die $obj->error;
$obj->baz or die $obj->error;

你可以直接写

$obj->foo;
$obj->bar;
$obj->baz;

知道如果有错误,你会被告知。而且在你之上的任何人都会被通知并可以捕获它。由于这是最常见的情况,让它发生而无需用户记住并一遍又一遍地输入。

如果您想忽略或从错误中恢复,只需使用eval BLOCK即可。

eval { $obj->foo };   # don't care if it works or not
eval { $obj->bar } or do { ...something when it doesn't work... };

由于这是一个特殊情况,用户必须记住添加更多代码并进行更多的工作。

这种方法的例子包括 DBI 的 RaiseError 标志,它默认情况下仅关闭以实现向后兼容性,以及出色的 autodie 模块。


0
如果您只有一个变量来存储错误,那么在检查错误变量的状态之前,如果程序中发生了多个错误,就会出现问题。如果可能的话,应该避免这种情况。
幸运的是,在 Perl 中,您可以避免这种情况。一个非常好的解决方案是使用Error.pm提供的面向对象的异常处理。这个模块将允许您编写 try/catch 块,像这样:
try {
    some code;
    code that might thrown an exception;
    more code;
    return;
}
catch Error with {
    my $ex = shift;   # Get hold of the exception object
    # handle the exception;
};

这个模块的 CPAN 文档 相当不错,同时 Perl.com 上也有相关文章。


我也赞成使用异常,但不要使用Error。改用Exception::Class,它更加强大。 - jplindstrom

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