Perl:将文本输出解析为哈希数组

Perl: parsing text output into array of hashes

我没有经验,也不擅长编写代码,而且我已经在这个问题上停留了一段时间了。我以前搜索过 Whosebug,但找不到任何与我的问题相匹配的东西,所以想尝试通过这种方式获得一些帮助。

我想构建一个脚本来分析某个命令的文本输出并将其放入一个哈希数组中,以便我可以用它做一些事情。

我有这样的文本输出:

      1 : DISPLAY AUTHREC GROUP('GROUP1')
 AMQ8864I: Display authority record details.
    PROFILE(SYSTEM.ADMIN.COMMAND.QUEUE)     ENTITY(GROUP1)
    ENTTYPE(GROUP)                          OBJTYPE(QUEUE)
    AUTHLIST(BROWSE,CHG,CLR,DLT,DSP,GET,INQ,PUT)
 AMQ8864I: Display authority record details.
    PROFILE(SYSTEM.MQEXPLORER.REPLY.MODEL)
    ENTITY(GROUP1)                         ENTTYPE(GROUP)
    OBJTYPE(QUEUE)
    AUTHLIST(BROWSE,CHG,CLR,DLT,DSP,GET,INQ,PUT)
 AMQ8864I: Display authority record details.
    PROFILE(self)                           ENTITY(GROUP1)
    ENTTYPE(GROUP)                          OBJTYPE(QMGR)
    AUTHLIST(ALTUSR,CHG,CONNECT,DLT,DSP,INQ,SET)

我想将每条记录存储为散列数组中的散列。 这就是我目前的做法(请不要对我写这篇文章的方式持否定态度,可能有 100 种更好的方法可以做到这一点,但我仍在学习,所以请记住这一点,随时欢迎提出改进建议):

    my @authrecs;   
    my @authrecoutput;  #this array contains the text output as displayed above, each line in a different element. 
    
    my $len = @authrecoutput;
    for (my $i = 0; $i <= $len; $i++){
        if(@authrecoutput[$i] =~ /^AMQ8864I/)  #when the eventcode occurs i start creating a new hash and store all properties in it. 
        {
            my $rec = {};
            while (@authrecoutput[$i+1] =~ /(\S+)\((.+?)\)/g ){
                $rec->{} = ;
            }
            while (@authrecoutput[$i+2] =~ /(\S+)\((.+?)\)/g ){ 
                $rec->{} = ;
            }
            while (@authrecoutput[$i+3] =~ /(\S+)\((.+?)\)/g ){  #problem with this that now the script only looks at $i+3 lines so anything on +4 i will miss.  
                $rec->{} = ;
            }
            push @authrecs, $rec; 
        }
    
    }

    print Dumper @authrecs;

我的输出:

$VAR1 = {
          'AUTHLIST' => 'BROWSE,CHG,CLR,DLT,DSP,GET,INQ,PUT,PASSALL,PASSID,SET,SETALL,SETID',
          'ENTTYPE' => 'GROUP',
          'ENTITY' => 'GROUP1',
          'OBJTYPE' => 'QUEUE',
          'PROFILE' => 'SYSTEM.ADMIN.COMMAND.QUEUE'
        };
$VAR2 = {
          'PROFILE' => 'SYSTEM.MQEXPLORER.REPLY.MODEL',
          'ENTITY' => 'GROUP1',
          'OBJTYPE' => 'QUEUE',
          'ENTTYPE' => 'GROUP'
        };
$VAR3 = {
          'PROFILE' => 'self',
          'OBJTYPE' => 'QMGR',
          'ENTITY' => 'GROUP1',
          'ENTTYPE' => 'GROUP',
          'AUTHLIST' => 'ALTUSR,CHG,CONNECT,DLT,DSP,INQ,SET,SETALL,SETID,CTRL,SYSTEM'
        };

这行得通,但问题是在数组中查找属性不是动态的,而是设置为@authrecoutput[$i+3] 的固定长度。例如,'SYSTEM.MQEXPLORER.REPLY.MODEL' 配置文件在文本输出中有 4 行,第四行包括 'AUTHLIST'属性 显然没有被捕获。

我尝试嵌套第二个 for 循环,当事件代码 AMQ8864I 再次出现时它会中断(因为那时我知道正在显示一条新记录)但我没有找到如何在正则表达式条件下中断 for 循环,如果那是一件事。

我如何将其编码为更加动态并读取行,直到检测到下一次出现 'AMQ8864I'。

我使用变量 $inside 作为标志,告诉我是否在记录中。如果是,我将元组存储到数组的最后一个散列中。当一个新的部分开始时,我将一个空的散列推入数组。

#!/usr/bin/perl
use warnings;
use strict;

my @arr;
my $inside;
while (<DATA>) {
    $inside = 0 unless /^ {4}/;

    if ($inside) {
        $arr[-1]{} =  while / (\S+)\(([^)]+)\)/g;
    }

    if (/^ AMQ8864I:/) {
        $inside = 1;
        push @arr, {};
    }
}
use Data::Dumper;
print Dumper \@arr;

__DATA__
...

输出:

$VAR1 = [
          {
            'ENTTYPE' => 'GROUP',
            'AUTHLIST' => 'BROWSE,CHG,CLR,DLT,DSP,GET,INQ,PUT',
            'OBJTYPE' => 'QUEUE',
            'PROFILE' => 'SYSTEM.ADMIN.COMMAND.QUEUE',
            'ENTITY' => 'GROUP1'
          },
          {
            'OBJTYPE' => 'QUEUE',
            'ENTITY' => 'GROUP1',
            'PROFILE' => 'SYSTEM.MQEXPLORER.REPLY.MODEL',
            'AUTHLIST' => 'BROWSE,CHG,CLR,DLT,DSP,GET,INQ,PUT',
            'ENTTYPE' => 'GROUP'
          },
          {
            'ENTITY' => 'GROUP1',
            'OBJTYPE' => 'QMGR',
            'PROFILE' => 'self',
            'AUTHLIST' => 'ALTUSR,CHG,CONNECT,DLT,DSP,INQ,SET',
            'ENTTYPE' => 'GROUP'
          }
        ];

利用匹配范围运算符提供了另一种解决方法。

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my $id = 'AMQ8864I';
my(%data, $record);

while( <DATA> ) {
    if( /^ $id:/ .. /^\s+AUTHLIST/ ) {
        unless( /^ $id:/ ) {
            $record->{} =  while /\s+(.+?)\((.*?)\)/g;
        } else {
            push @{$data{$id}}, $record if defined $record;
            $record = undef;
        }
    }
}

push @{$data{$id}}, $record if defined $record;

say Dumper(\%data);

__DATA__
     1 : DISPLAY AUTHREC GROUP('GROUP1')
 AMQ8864I: Display authority record details.
    PROFILE(SYSTEM.ADMIN.COMMAND.QUEUE)     ENTITY(GROUP1)
    ENTTYPE(GROUP)                          OBJTYPE(QUEUE)
    AUTHLIST(BROWSE,CHG,CLR,DLT,DSP,GET,INQ,PUT)
 AMQ8864I: Display authority record details.
    PROFILE(SYSTEM.MQEXPLORER.REPLY.MODEL)
    ENTITY(GROUP1)                         ENTTYPE(GROUP)
    OBJTYPE(QUEUE)
    AUTHLIST(BROWSE,CHG,CLR,DLT,DSP,GET,INQ,PUT)
 AMQ8864I: Display authority record details.
    PROFILE(self)                           ENTITY(GROUP1)
    ENTTYPE(GROUP)                          OBJTYPE(QMGR)
    AUTHLIST(ALTUSR,CHG,CONNECT,DLT,DSP,INQ,SET)

输出

$VAR1 = {
          'AMQ8864I' => [
                          {
                            'ENTTYPE' => 'GROUP',
                            'AUTHLIST' => 'BROWSE,CHG,CLR,DLT,DSP,GET,INQ,PUT',
                            'OBJTYPE' => 'QUEUE',
                            'PROFILE' => 'SYSTEM.ADMIN.COMMAND.QUEUE',
                            'ENTITY' => 'GROUP1'
                          },
                          {
                            'ENTTYPE' => 'GROUP',
                            'ENTITY' => 'GROUP1',
                            'OBJTYPE' => 'QUEUE',
                            'PROFILE' => 'SYSTEM.MQEXPLORER.REPLY.MODEL',
                            'AUTHLIST' => 'BROWSE,CHG,CLR,DLT,DSP,GET,INQ,PUT'
                          },
                          {
                            'ENTTYPE' => 'GROUP',
                            'AUTHLIST' => 'ALTUSR,CHG,CONNECT,DLT,DSP,INQ,SET',
                            'OBJTYPE' => 'QMGR',
                            'PROFILE' => 'self',
                            'ENTITY' => 'GROUP1'
                          }
                        ]
        };