Reputation: 35
i'm not experienced and not very good at writing code and I have been stuck on this issue for a little while now. I have searched stackoverflow before but couldn't really find anything that matches my issue so thought of trying to get some help this way.
I want to build a script that analyzes text output from a certain command and put that into an array of hashes so i can do stuff with it.
I have text output like this:
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)
I want to store each record as a hash on an array of hashes. this is how i currently do it (please don't be negative about how i wrote this, there are probably 100 better ways to do this but i'm still learning so keep that in mind please, suggestions to improve are always welcome) :
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->{$1} = $2;
}
while (@authrecoutput[$i+2] =~ /(\S+)\((.+?)\)/g ){
$rec->{$1} = $2;
}
while (@authrecoutput[$i+3] =~ /(\S+)\((.+?)\)/g ){ #problem with this that now the script only looks at $i+3 lines so anything on $1+4 i will miss.
$rec->{$1} = $2;
}
push @authrecs, $rec;
}
}
print Dumper @authrecs;
my output:
$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'
};
This works but the problem is that looking for the properties in the array is not dynamic and is set at a fixed length of @authrecoutput[$i+3]. For example, the 'SYSTEM.MQEXPLORER.REPLY.MODEL' profile has 4 lines in the text output and the fourth line including the 'AUTHLIST'property is obviously not captured.
I tried nesting a second for loop which breaks when the eventcode AMQ8864I occurs again (because that's when I know a new record is being displayed) but i didn't find how to break a for loop on a regex condition, if that is even a thing.
How could i code this to be more dynamically and reads lines until the next occurrence of 'AMQ8864I' is detected.
Upvotes: 1
Views: 197
Reputation: 6808
Utilization of match range operator provides other approach to the solution.
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->{$1} = $2 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)
Output
$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'
}
]
};
Upvotes: 1
Reputation: 241988
I use a variable $inside
as a flag that tells me whether I'm inside a record or not. If I am, I store the tuples into the last hash in the array. When a new section starts, I push an empty hash into the array.
#!/usr/bin/perl
use warnings;
use strict;
my @arr;
my $inside;
while (<DATA>) {
$inside = 0 unless /^ {4}/;
if ($inside) {
$arr[-1]{$1} = $2 while / (\S+)\(([^)]+)\)/g;
}
if (/^ AMQ8864I:/) {
$inside = 1;
push @arr, {};
}
}
use Data::Dumper;
print Dumper \@arr;
__DATA__
...
Output:
$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'
}
];
Upvotes: 4