在perl中,异常处理使用 eval { statements; }; if( $@ ) {do something } 结构捕获异常。
这里有一点必须要注意:就是 eval 语句块内的 return 语句只从 eval 块内返回,并不会从它所在的方法中返回,即 eval {} 块内的 return 语句只退出当前 eval 块,并不会退出当前函数或方法。示例如下:
#!/usr/bin/perl -w
sub fun
{
eval {
print "In eval block\n";
return;
};
if ($@) {
print "Exception happened\n";
}
print "Leave the eval block\n";
return;
print "The end of the function\n";
}
这里输出如下:
test@Linux[/home/]./test.pl
In eval block
Leave the eval block
即 eval 中的 return 语句是不会从当前方法中返回的,它只返回 eval 语句块而已。
我的理解是因为Perl把eval语句块中的代码当做一小段单独的perl程序来执行(甚至可以认为与当前的perl程序并无关系)。
又因为这段代码是在当前perl程序环境中运行的,因此它可以看到并使用(并且可以修改)任何全局的变量定义,并且在 eval 代码块结束以后,任何非局部变量的设置仍然有效。
正因为上面的原因,perl -c filename.pl 不能检测出任何 eval{} 语句块中的语法错误。
因为 eval}{} 语句块中的内容是在perl程序运行过程中被当做一段独立程序运行,因此perl的语法检查功能check会跳过eval块中内容,eval{}中的内容只有在运行时才会被解析并执行,并由perl解析器捕获其中可能产生的异常。
如果有一个可捕获的错误存在(包括任何由 die 操作符生成的),eval 返回 undef 并且把错误信息放到 $@ 里。如果没有错误,Perl 保证把 $@ 设置为空字串。
这里要注意:
如果 eval 语句块位于子过程(sub)之内, 则eval语句中的return语句只会从eval小语句块中返回, 并不会从当前过程中返回。
当然, 如果eval语句块中包含exit方法, 则无论eval位于子过程(sub)之内还是位于全局文件作用域,都会导致整个程序退出。
(当然,按照 exit 方法的约定,主程序退出之前会先执行任何已定义的 END 过程)
但是,eval{}语句之外的if($@)语句则是perl脚本本身的一部分,因此在 if($@) 语句块中可以直接返回当前子过程,其中的语句更是会被 perl 的check规则检查。
这里的一个错误程序示例,用于取得程序的运行日志表中的日志信息。(日志信息为CLOB类型)
sub get_inst_log
{
my $inst_id = shift @_;
my $sql = "select app_log from ctl.ta_etl_app_log where inst_id=$inst_id with ur";
eval {
my @res = $dbh->execsel_ctl( $sql );
if (@res) {
return substr( $dbh->trim( $res[0]->[0] ), 0, 3888 );
}
else {
return "null";
}
};
if ($@) {
return "null";
}
}
上面代码看起来好像没有问题,实际上却是错误的,因为它永远返回都是"null"或空。
这是因为,上面的 if( @res )语句块中的 return 语句,只能从 eval 语句块中返回,而不是从sub子过程返回。
eval语句块执行结束后,会继续执行下面的内容, 即 if( $@ )部分。
如果前边发生了异常,则函数返回值为 "null";如果前边没有发生异常,则函数返回值为最后一条语句的执行结果返回值,这里最后一条语句的返回值为空,因此整个函数的返回值为空。
正确的代码应该如下:
sub get_inst_log
{
my $inst_id = shift @_;
#这里的app可以用cast(app_log as varchar(4000))替代,
#用于显式把clob类型转为varchar(4000)
my $sql = "select app_log from ctl.ta_etl_app_log where inst_id=$inst_id with ur";
my $ret = "null";
eval {
my @res = $dbh->execsel_ctl( $sql );
if (@res) {
$ret = substr( $dbh->trim( $res[0]->[0] ), 0, 3888 );
}
};
if ($@) {
$ret = "null";
}
return $ret;
}
即PERL中函数的返回值不能从 eval{} 语句块中返回,eval语句块只能用于修改变量的值,而不能用于返回变量的值。
由于eval语句块运行于 perl 的程序环境中,因此可以修改 perl 的局部或全局变量的值,上面第二种写法符合要求。