perl环视(断言)


每次写断言相关匹配都查一下文档,还老实点记在自己blog吧

$ vi test.pl  
#!/usr/bin/perl
USE warnings;
USE strict;
my $line = "alovebcliked";
#(?=EXP)断言自身出现的位置的后面能匹配表达式EXP
IF ( $line =~ /(\w+)(?=love)/ ){
  print "matched one:$1\n";
}
#(?<=EXP)断言自身出现的位置的前面能匹配表达式EXP
IF ($line =~ /(?<=LIKE)\d/){
  print "matched two\n";
}
elsif($line =~ /(?<=LIKE)(\w+)/){
  print "matched two two:$1\n";
}
 
$line = "abc123";
#(?!EXP)断言此位置的后面不能匹配表达式EXP
IF ($line =~ /([a-z]{2})(?!\d+)/){
   print "matched three:$1\n";
}
IF ($line =~ /([a-z]{3})(?!\d+)/){
   print "matched three three:$1\n";
}
$line = "123abc";
#(?<!EXP)断言此位置的前面不能匹配表达式
IF ($line =~ /(?<![a-z])([a-z]{3})/){
   print "matched four:$1\n";
}
$ perl test.pl
matched one:a
matched two two:d
matched three:ab
matched four:abc

perl私有方法两种实现


perl不像其他语言有private声明私有方法,但有变通方法。

一、利用调用者是否是同一个类,来隐藏私有方法(参考http://stackoverflow.com/questions/451521/how-do-i-make-private-functions-in-a-perl-module)

$cat test_priv.pl
package My;
sub new {
        return bless { }, shift;
}
sub private_func {
        my ($s,%args) = @_;
        die "Error: Private method called"
                unless (caller)[0]->isa( ref($s) );
        warn "OK: Private method called by " . (caller)[0];
}
sub public_func {
        my ($s,%args) = @_;
        $s->private_func();
}
package main;
my $obj = My->new();
$obj->public_func();
$obj->private_func();
 
$perl test_priv.pl
OK: Private method called by My at test_priv.pl line 9.
Error: Private method called at test_priv.pl line 7.

生成对象只能通过公共方法才能访问私有方法。

二、利用文件作用域,perl只能继承类的方法的特性

$cat test_priv2.pl
package Test;
sub new {
        return bless { }, shift;
}
my $private_func = sub {
   my $self = shift;
   print "I am private func!\n";
};
sub public_func {
   my $self = shift;
   print "I am public func!\n";
   $self->$private_func();
}
my $obj = Test->new();
$obj->public_func;
 
$perl test_priv2.pl
I am public func!
I am private func!

类继承以后,也能通过public_func来访问匿名函数(private)

caller函数


在写perl中,调试代码要查看是谁调用谁,caller就比较方便实现这个功能

($package, $filename, $line) = caller;
返回包名、文件名、调用的行号

caller返回一数组,可以数组下标来取具体值

$cat caller.pl
#!/usr/bin/perl
#use strict;
#use warnings;
sub b{
    my $sub=(caller(0))[3];
    print "subroutine $sub\n";
    print caller()."\n";
    print caller(0);
}
&b;
 
$perl caller.pl
subroutine main::b
main
maincaller.pl10main::b0256

解释:caller(0)[3]取得是子程序名,caller中参数0代表当前堆栈

$cat caller.pl 
#!/usr/bin/perl
#use strict;
#use warnings;
sub c{
    my $sub=(caller(0))[3];
    print "subroutine $sub\n";
    print caller(1);
    print "\n";
    print caller(2);
    print "\n";
}
sub b{
    my $sub=(caller(0))[3];
    print "subroutine $sub\n";
    print caller(1);
    print "\n";
    &c
}
sub a{
    my $sub=(caller(0))[3];
    print "subroutine $sub\n";
    &b;
}
&a;
 
$perl caller.pl 
subroutine main::a
subroutine main::b
maincaller.pl24main::a0256
subroutine main::c
maincaller.pl22main::b0256
maincaller.pl24main::a0256

解释:caller(1)是返回调用者、caller(2)返回调用的调用者

参考:
http://perldoc.perl.org/functions/caller.html
http://blog.csdn.net/ace_fei/article/details/6851882

perl查询空表引起的bug


在ali集团对数据库的数据采集、监控、分析等由xxagent完成,所以对xxagent运行稳定非常重要。下面介绍xxagent一个bug,导致数据查询异常。

1、数据库的lijie表中没有记录:

root@xx 10:28:26>show create table lijie \G;
*************************** 1. row ***************************
       Table: lijie
Create Table: CREATE TABLE `lijie` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(32) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=gbk;
root@xx 10:28:30>select count(*) from lijie;
+----------+
| count(*) |
+----------+
|        0 |
+----------+

2、执行以下代码:

my $sql3 = "select id,name from lijie";
my @sql3re = DB::query($sql3);
$Common::log->debug("luoxuansun3->@sql3re->") if (@sql3re);
$Common::log->debug("luoxuansun4") if (DB::query($sql3));
my $sql3n  = scalar @sql3re;
my $sql3nn = @sql3re;
my $sql3nnn = DB::query($sql3);
$Common::log->debug("luoxuansun5"."->".$sql3n."->".$sql3nn."->".$sql3nnn."aa");

3、log信息

$xxagent logf|grep luoxuansun
2013-07-28 10:39:50 [DEBUG] luoxuansun3->-> 
2013-07-28 10:39:50 [DEBUG] luoxuansun5->1->1->aa
2013-07-28 10:39:52 [DEBUG] luoxuansun3->->
2013-07-28 10:39:52 [DEBUG] luoxuansun5->1->1->aa

4、现象
通过执行DB模块的query函数返回给@sql3re后,里面既然有了记录(实际是没有记录),但直接执行”DB::query($sql3)”却是没有数据,前后矛盾

5、写入一条记录

root@xx 10:28:57>insert into lijie values(1,'a');
Query OK, 1 row affected (0.00 sec)
log信息
2013-07-28 10:44:39 [DEBUG] luoxuansun3->HASH(0x1bbb1f8)-> 
2013-07-28 10:44:39 [DEBUG] luoxuansun4
2013-07-28 10:44:39 [DEBUG] luoxuansun5->1->1->1aa
注意:结果"luoxuansun5->1->1->1aa"并没有发生变化,但"luoxuansun4"打印出来,说明的确有数据存在

6、再写一条记录

root@xx 10:44:35>insert into lijie values(1,'b');
Query OK, 1 row affected (0.00 sec)
log信息
2013-07-28 10:46:53 [DEBUG] luoxuansun3->HASH(0x1bd6628) HASH(0x1bd7df8)->  
2013-07-28 10:46:53 [DEBUG] luoxuansun4 at  
2013-07-28 10:46:53 [DEBUG] luoxuansun5->2->2->2aa 
注意:结果集中有两条记录了,变化符合预期

7、排查DB的query函数

sub query { 
  my $sql = join '', @_; 
  if ( !defined($dbh) ) {
    $dbh = _connect;
  }
  for ( my $count = 1 ; $count <= $connect_retries ; $count++ ) {
    if ( defined($dbh) ) {
      my @res = $dbh->query($sql);
      my $x = scalar @res;
      $Common::log->debug("luoxuansat>$sql->$x");
      if ( scalar(@res) ) {
        $Common::log->debug("luoxuansat2->$sql->$x");
        return @res;
      } else {
        $dbh = _connect;
        $Common::log->error("query retires $count $sql"); 
        next;   
      }
    } else {
      $dbh = _connect;
      $Common::log->error("query retires $count $sql"); 
      next;   
    }
  }
}
 
问题是出在空表时,先把记录删除,看一下log信息
root@xx 10:46:44>delete from lijie;
Query OK, 2 rows affected (0.00 sec)
log信息
$xxagent logf|grep luoxuansat|grep lijie
2013-07-28 10:53:31 [DEBUG] luoxuansat>select id,name from lijie->0 
2013-07-28 10:53:31 [DEBUG] luoxuansat>select id,name from lijie->0
注意:调用$dbh->query返回记录集为0,那说问题就出在DB的query函数上了
log信息
$xxagent logf|grep "query retires"|grep lijie
2013-07-28 10:56:18 [ERROR] query retires 1 select id,name from lijie 
2013-07-28 10:56:18 [ERROR] query retires 2 select id,name from lijie 
2013-07-28 10:56:18 [ERROR] query retires 3 select id,name from lijie 
注意:结果为0的情况下,却走到了:
else {
        $dbh = _connect;
        $Common::log->error("query retires $count $sql"); 
        next;   
      }
而没有结果集返回

8、bug fixed:
但没有结果返回时,应该明确返回空数组给调用方或对空结果做特殊处理

sub query { 
  my $sql = join '', @_; 
  my @nores;   --设置空数组
  if ( !defined($dbh) ) {
    $dbh = _connect;
  }
  for ( my $count = 1 ; $count <= $connect_retries ; $count++ ) {
    if ( defined($dbh) ) {
      my @res = $dbh->query($sql);
      my $x = scalar @res;
      $Common::log->debug("luoxuansat>$sql->$x");
      if ( scalar(@res) ) {
        $Common::log->debug("luoxuansat2->$sql->$x");
        return @res;
      } else {
        $dbh = _connect;
        $Common::log->error("query retires $count $sql"); 
        next;   
      }
    } else {
      $dbh = _connect;
      $Common::log->error("query retires $count $sql"); 
      next;   
    }
   return @nores;--无结果集时返回
  }

9、修改效果

$xxagent logf|grep luoxuansun
2013-07-28 11:06:45 [DEBUG] luoxuansun5->0->0->0aa 
2013-07-28 11:06:48 [DEBUG] luoxuansun5->0->0->0aa 
2013-07-28 11:07:30 [DEBUG] luoxuansun5->0->0->0aa 
下面代码不再执行打印
$Common::log->debug("luoxuansun3->@sql3re->") if (@sql3re);
写入一条记录
root@xx 11:11:05>insert into lijie values(1,'a');
Query OK, 1 row affected (0.00 sec)
log信息
$xxagent logf|grep luoxuansun
2013-07-28 11:12:14 [DEBUG] luoxuansun3->HASH(0x212f5f0)->  
2013-07-28 11:12:14 [DEBUG] luoxuansun4 at  
2013-07-28 11:12:14 [DEBUG] luoxuansun5->1->1->1aa 
#xxagent logf|grep luoxuansat|grep lijie
2013-07-28 11:12:14 [DEBUG] luoxuansat>select id,name from lijie->1  
2013-07-28 11:12:14 [DEBUG] luoxuansat2->select id,name from lijie->1  
再写入一条
root@xx 11:11:11>insert into lijie values(1,'b');
Query OK, 1 row affected (0.00 sec)
log信息
$xxagent logf|grep luoxuansun
2013-07-28 11:14:51 [DEBUG] luoxuansun3->HASH(0x2140e58) HASH(0x21408a0)->  
2013-07-28 11:14:51 [DEBUG] luoxuansun4 at  
2013-07-28 11:14:51 [DEBUG] luoxuansun5->2->2->2aa 
#xxagent logf|grep luoxuansat|grep lijie
2013-07-28 11:14:51 [DEBUG] luoxuansat>select id,name from lijie->2  
2013-07-28 11:14:51 [DEBUG] luoxuansat2->select id,name from lijie->2

10、小结
上面把很多代码都省略,其实原本的代码函数之间调用还要复杂,为了把问题尽量简单,没有例出。问题总是有它的原因,只要你坚持。

log4perl日志模块使用


1