使用eval时的注意事项

在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 的局部或全局变量的值,上面第二种写法符合要求。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值