如何使用Perl解析runmqsc命令输出?

时间:2009-08-26 19:35:52

标签: regex perl

我正在尝试设计Perl regex来解析来自IBM的runmqsc实用程序的命令输出。

每个感兴趣的输出行包含一个或多个属性/值对,格式为:“ATTRIBUTE(VALUE)”。属性的值可以为空,也可以包含括号本身。通常,给定行上最多出现两个属性/值对,因此正则表达式是在此假设下编写的。

Perl RE的示例输入:

CHANNEL(TO.IPTWX01)                     CHLTYPE(CLUSRCVR)  
DISCINT(6000)                           SHORTRTY(10)  
TRPTYPE(TCP)                            DESCR( )  
LONGTMR(1200)                           SCYEXIT( )  
CONNAME(NODE(1414))                     MREXIT( )  
MREXIT( )                               CONNAME2(SOME(1416))  
TPNAME( )                               BATCHSZ(50)  
MCANAME( )                              MODENAME( )  
ALTTIME(00.41.56)                       SSLPEER()  
CONTRIVED()                             ATTR (00-41-56)   
CONTRIVED()                             DOCTORED()  
MSGEXIT( )   

我有以下Perl代码来捕获每个属性/值对。

Perl代码

my $resplit = qr/\s+([^\s]+(?:\([^)]*\))?)\s?/;  
while ( <IN2> )  
{ s/[\s\r\n]+$//;  
  if ( m/^\s(?:$resplit)(?:$resplit)?$/ )  
  { my ($one,$two) = ($1,$2);  
    print "one: $one, two: $two\n";  
  }  
} 

以上代码应用于样本输入时的输出:

one: CHANNEL(TO.IPTWX01), two: CHLTYPE(CLUSRCVR)  
one: DISCINT(6000), two: SHORTRTY(10)  
one: TRPTYPE(TCP), two: DESCR( )  
one: LONGTMR(1200), two: SCYEXIT( )   
one: CONNAME(NODE(1414)), two: MREXIT( )   
one: MREXIT( ), two: CONNAME2(SOME(1416))   
one: TPNAME( ), two: BATCHSZ(50)  
one: MCANAME( ), two: MODENAME( )  
one: ALTTIME(00.41.56), two: SSLPEER()   
one: CONTRIVED(), two: ATTR(00-41-56)   
one: CONTRIVED(), two: DOCTORED()   
one: MSGEXIT(, two: )   

除了输出中的最后一行之外,这很有用 以上。我真的很难弄清楚如何 修改上面的表达式$ resplit来捕获最后一种情况。

任何人都可以提供有关如何完成这项工作的任何想法/建议 另一种方法?

4 个答案:

答案 0 :(得分:5)

Text::Balanced模块旨在解决此类问题。这种方法也可以处理任意数量的列。

use strict;
use warnings;
use Text::Balanced qw(extract_bracketed);

my ($extracted, $remainder, $prefix);
while ( defined($remainder = <DATA>) ){
    while ( Get_paren_text() ){
        $prefix =~ s/ //g;
        print $prefix, $extracted, "\n";
    }
}
sub Get_paren_text {
    ($extracted, $remainder, $prefix) 
        = extract_bracketed($remainder, '()', '[\w ]+');
    return defined $extracted;
}

__DATA__
CHANNEL(TO.IPTWX01)  CHLTYPE(CLUSRCVR)      FOO( ( BAR) )
DISCINT(6000)        SHORTRTY(10)           BIZZ((((BUZZ) ) ) ) )
TRPTYPE(TCP)         DESCR( )               
LONGTMR(1200)        SCYEXIT( )             
CONNAME(NODE(1414))  MREXIT( )              
MREXIT( )            CONNAME2(SOME(1416))   
TPNAME( )            BATCHSZ(50)            
MCANAME( )           MODENAME( )            
ALTTIME(00.41.56)    SSLPEER()              
CONTRIVED()          ATTR (00-41-56)        
CONTRIVED()          DOCTORED()             
MSGEXIT( )

答案 1 :(得分:3)

我想尝试使用Regexp::Grammars

所以这是:

#! /opt/perl/bin/perl
use strict;
#use warnings;
use 5.10.1;

use Regexp::Grammars;

my $grammar = qr{
  <line>

  <token: line>
    (?: <[pair]> \s* )+

    (?{
      my $arr = $MATCH{pair};
      local $MATCH = {};

      for my $pair( @$arr ){
        my($key)   = keys   %$pair;
        my($value) = values %$pair;
        $MATCH->{$key} = $value;
      }
    })

  <token: pair>
    <attrib> \s* \( \s* <value> \s* \)
    (?{
      $MATCH = {
        $MATCH{attrib} => $MATCH{value}
      };
    })

  <token: attrib>
    [^()]*?

  <token: value>
    (?:
      <MATCH=pair> |
      [^()]*?
    )
}x;

use warnings;

my %attr;
while( my $line = <> ){
  $line =~ /$grammar/;
  for my $key ( keys %{ $/{line} } ){
    $attr{$key} = $/{line}{$key};
  }
}

use YAML;

say Dump \%attr;
---
ALTTIME: 00.41.56
ATTR: 00-41-56
BATCHSZ: 50
CHANNEL: TO.IPTWX01
CHLTYPE: CLUSRCVR
CONNAME:
  NODE: 1414
CONNAME2:
  SOME: 1416
CONTRIVED: ''
DESCR: ''
DISCINT: 6000
DOCTORED: ''
LONGTMR: 1200
MCANAME: ''
MODENAME: ''
MREXIT: ''
MSGEXIT: ''
SCYEXIT: ''
SHORTRTY: 10
SSLPEER: ''
TPNAME: ''
TRPTYPE: TCP

答案 2 :(得分:1)

while ( <IN2> ) {
    while ( /([A-Z]+)\s*(\((?:[^()]*+|(?2))*\))/g ) {
        print "$1$2\n";
    }
}

这适用于嵌套的parens,例如

CONNAME(NODE(1414, SOME(1416) ) )           ATTR (00-41-56)

(?2)部分是递归的,* +表示“不回溯” - 仅适用于Perl 5.10或更高版本;我是从http://faq.perl.org/perlfaq6.html#Can_I_use_Perl_regul

得到的

答案 3 :(得分:0)

#!/usr/bin/perl

use strict;
use warnings;

my @parsed;

while ( my $line = <DATA> ) {
    while ( $line =~ / ([A-Z0-9]+) \s* \( (.*?) \) \s /gx ) {
        push @parsed, { $1 => $2 }
    }
}

use Data::Dumper;
print Dumper \@parsed;

__DATA__
CHANNEL(TO.IPTWX01)                     CHLTYPE(CLUSRCVR)
DISCINT(6000)                           SHORTRTY(10)
TRPTYPE(TCP)                            DESCR( )
LONGTMR(1200)                           SCYEXIT( )
CONNAME(NODE(1414))                     MREXIT( )
MREXIT( )                               CONNAME2(SOME(1416))
TPNAME( )                               BATCHSZ(50)
MCANAME( )                              MODENAME( )
ALTTIME(00.41.56)                       SSLPEER()
CONTRIVED()                             ATTR (00-41-56)
CONTRIVED()                             DOCTORED()
MSGEXIT( )