How can I parse the output of the runmqsc command using Perl?

I am trying to develop a Perl regex to parse the command output from the IBM runmqsc utility.

Each line of interest contains one or more attribute / value pairs with the format: "ATTRIBUTE (VALUE)". The attribute value can be empty or contain parentheses. Typically, there are at most two attribute / value pairs appearing on a given line, so a regular expression is written in this assumption.

Example input in 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( )   

      

I have the following Perl code to capture each attribute / value pair.

Perl code

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";  
  }  
} 

      

Here's the output when the above code is applied to sample input:

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:)   

This works great except for the last line in the output above. I'm really trying to figure out how to change the above $ resplit expression to capture the latter case.

Can anyone suggest any ideas / suggestions on how to make this work or a different approach?

+2


source to share


4 answers


The Text :: Balanced module addresses this problem. This approach will handle any number of columns.



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( )

      

+5


source


I wanted to try and use Regexp::Grammars

.

So here it is:



#! /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
+3


source


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

      

This works for nested partners like

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

      

The (? 2) part is recursive, * + means "don't back out" - only works in Perl 5.10 or later; I got this from http://faq.perl.org/perlfaq6.html#Can_I_use_Perl_regul

+1


source


#!/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( )

      

0


source







All Articles