为什么即使我在eval中包装了DBI调用,控制台仍然显示DBI错误?

3

我有一个数据库查询,在eval中运行,以捕获错误。问题是,即使被捕获,错误消息也会输出到控制台上。我该如何停止错误消息这样做,因为我想自己解析它并返回我的自定义消息?

my $dbh = DBI->connect('dbi:Pg:dbname=database;host=localhost',
    'user', 'pass', 
    {RaiseError => 1}
);

eval{
    $sth = $dbh->prepare($sql);
    $sth->execute;
};

if($@){
    #Do my parse/print stuff here I know
}
3个回答

14

忽略错误,无论它们是否致命,都不是一个好主意。而且,以您目前的方式检查 $@ 也不可取(请参阅有关perl异常的本网站上的问题,了解更好的捕获异常的方法;我在下面使用 Try::Tiny,这可能是所有路线中最轻量级的)。

如果早期操作失败,不建议继续进行DBI操作,应该在每个步骤检查错误条件:

use strict; use warnings;
use Try::Tiny;

try {
    my $sth = $dbh->prepare($sql) or die $dbh->errstr;
    $sth->execute or die $sth->errstr;
} catch {
    print "got error $_\n";
    # return from function, or do something else to handle error
};

记住,在每个模块和脚本中,始终使用 use strict; use warnings;。您的代码摘录表明您尚未这样做。


2
我的代码示例是我正在使用的最小可行示例。因此,像警告和严格模式之类的内容以及其他 eval 语句都已被删除。但还是感谢你参与了这个问题的回答。你提供的 Try:Tiny 链接避免了完全抨击这个问题并被我 downvote 的命运。 - Ben Dauphinee
6
我从 $sth = ... 而不是 my $sth = ... 推断出这一点。没必要这么粗鲁。 - Ether
2
和你一样。仅凭七行代码就假设脚本的构建方式是相当粗鲁的。 - Ben Dauphinee
19
我们只能看到你发布的内容。 - Sinan Ünür

6
你可以在你的connect调用中指定 'PrintError => 0'(或使用HandleError):
my $dbh = DBI->connect('dbi:Pg:dbname=database;host=localhost', $user, $passwd, {
  PrintError => 0,
  RaiseError => 1,
});

或者设置每个语句的句柄:
my $sth = $dbh->prepare("SELECT * from my_table");
$sth->{PrintError} = 0;
$sth->execute();
...etc.

此外,不要依赖于$@来指示错误。更好的使用eval的方法是:
my $result = eval {
  ...
  $sth->...etc.
  1;
}
unless ($result) {
  # Do error handling..log/print $@
}

2
如果你走的是(Print|Raise)Error路线,或许也可以使用HandleError - vol7ron
@vol7ron HandleError也是一种可能性,但我从来没有发现它的好处,因为我通常只是将代码块包装在eval {}中以捕获所有异常,而不仅仅是DBI错误。 - runrig
1
您也可以为语句句柄设置这些属性中的一些。 - brian d foy
更新:我现在经常使用HandleError,这样我就可以使用Carp::confess来获取堆栈跟踪。 - runrig

2
eval { }可以捕获致命错误(来自dieCarp::croak调用),但无法捕获非致命错误消息(来自warncarp)。如果要处理警告消息,请查看如何在%SIGwarn文档中安装警告处理程序。

一个简单的解决方法是在eval块内使用一个简单的警告处理程序。

eval {
    local $SIG{__WARN__} = sub { };
    ...
};

另请参阅:perlfaq7:如何暂时屏蔽警告?


2
你确定抑制警告信息是个好主意吗?Blah blah blah。 - mob
@mobrule:我认为这就是被问到的内容。local $SIG{__WARN__} = sub {} 是一个很明显的选择。http://perldoc.perl.org/functions/warn.html - vol7ron
虽然在 eval 块中抑制警告的方法是正确的,但在 connect 方法中指定 PrintError => 0 才是处理 OP 情况的正确方式。 - Sinan Ünür
它们两个都可以。这取决于 OP 想要什么 - 两个答案都是有效的,但是 DBI 内部使用 $SIG{__WARN__}Carp 吗? - vol7ron
不必全局设置警告处理程序,而是可以使用HandleError属性来处理特定语句的处理程序。您不希望仅仅因为一个对象出问题就改变其他所有东西所做的事情。 - brian d foy
@brian d foy:这就是为什么我建议在runrig所选答案的注释中使用HandleError。顺便说一句,你没有将其设置为全局变量,而是将其设置为eval块的本地变量。如果这是唯一调用的两个语句,那么这是一个完美的解决方案。有时候,我喜欢使用替代方案,只是为了提醒自己该功能存在,并在其他更有意义的地方使用它。 - vol7ron

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