perl - help with hash and dumping of records
I have a perl script that is only keeping the last set of records for a named set and I have more than one set of records. So its over writing the data in the hash and just keeping the last set. I need help in printing out all the records. Thanks!
Here's a copy of my script:
#!/usr/local/bin/perl
use strict;
use warnings;
use Data::Dumper;
my ($ServerName)=@ARGV;
my %MyItems;
foreach my $ServerName(@ARGV){
while (my $line = <>){
chomp $line;
if ($line =~ m/.* \w+ \d{2} (\d{2}:\d{2}:\d{2}) \d{4}: ([^:]+):backup:/){
my $ServerName = basename $ARGV, '.mydomain.com.backup-software.log'; #$ARGV is reading input from command line
my $BckupSet =$2;
my $BckupVal=$1;
$MyItems{$ServerName}{$BckupSet}->{'1-Server'} = $ServerName;
$MyItems{$ServerName}{$BckupSet}->{'2-BackupSet'} = $BckupSet;
$MyItems{$ServerName}{$BckupSet}->{'3-StartTime'} = $BckupVal;
if ($line =~ m/(backup-date)[:=](.+)/){
my $BckupKey="4-DateStamp";
my $BckupVal=$2;
$MyItems{$ServerName}{$BckupSet}->{$BckupKey} = $BckupVal;
}
if ($line =~ m/(backup-time)[:=](.+)/){
my $BckupKey="5-Duration";
my $BckupVal=$2;
$MyItems{$ServerName}{$BckupSet}->{$BckupKey} = $BckupVal;
}
if ($line =~ m/(backup-size)[:=](.+)/){
my $BckupKey="6-Size";
my $BckupVal=$2;
$MyItems{$ServerName}{$BckupSet}->{$BckupKey} = $BckupVal;
}
if ($line =~ m/(Backup succeeded)/){
my $BckupKey="7-Status";
my $BckupVal="Succeeded";
$MyItems{$ServerName}{$BckupSet}->{$BckupKey} = $BckupVal;
}
if ($line =~ m/(ERROR)[:=](.+)/){
my $BckupKey="8-Status";
my $BckupVal="Unsuccessful";
$MyItems{$ServerName}{$BckupSet}->{$BckupKey} = $BckupVal;
print "$BckupKey=$BckupVal\n" if debug;
}
}
} #endwhile
print Dumper(\%MyItems);
for my $ServerName(keys%MyItems){
for my $BckupSet(keys%{$MyItems{$ServerName}}){
for(sort keys%{$MyItems{$ServerName}{$BckupSet}}){
#print$_,'=>',$MyItems{$ServerName}{$BckupSet}{$_},';';
print$_,'=',$MyItems{$ServerName}{$BckupSet}{$_},';';
}
print"\n";
}
}
} #END foreach
Here's what it looks like when it dumps:
$VAR1 = {
'server1.name.colo' => {
'set1' => {
'3-StartTime' => '07:08:15',
'1-Server' => 'server1.name.colo',
'6-Size' => '72.04 GB',
'7-Status' => 'Succeeded',
'4-DateStamp' => '20100820060002',
'5-Duration' => '01:08:13',
'2-BackupSet' => 'set1',
'8-Status' => 'Unsuccessful'
},
'set2' => {
'7-Status' => 'Succeeded',
'6-Size' => '187.24 GB',
开发者_JS百科 '3-StartTime' => '01:51:25',
'4-DateStamp' => '20100820000003',
'1-Server' => 'server1.name.colo',
'5-Duration' => '01:51:21',
'2-BackupSet' => 'set2'
},
'set3' => {
'3-StartTime' => '23:00:05',
'4-DateStamp' => '20100814230003',
'1-Server' => 'server1.name.colo',
'8-Status' => 'Unsuccessful',
'2-BackupSet' => 'set3'
},
'set4' => {
'7-Status' => 'Succeeded',
'6-Size' => '427.75 GB',
'3-StartTime' => '00:43:20',
'4-DateStamp' => '20100819200004',
'1-Server' => 'server1.name.colo',
'5-Duration' => '04:43:14',
'2-BackupSet' => 'set4'
},
'set3' => {
'7-Status' => 'Succeeded',
'6-Size' => '46.42 GB',
'3-StartTime' => '04:42:59',
'4-DateStamp' => '20100820040002',
'1-Server' => 'server1.name.colo',
'5-Duration' => '00:42:56',
'2-BackupSet' => 'set3'
}
}
};
Based on the debug output, it looks like your problem is here:
if ($line =~ m/(ERROR)[:=](.+)/){
my $BckupKey="8-Status";
my $BckupVal="Unsuccessful";
$MyItems{$ServerName}{$BckupSet}->{$BckupKey} = $BckupVal;
print "$BckupKey=$BckupVal\n" if debug;
}
To save all errors, you'll need to treat that hash slot as a reference to an array:
if ($line =~ m/(ERROR)[:=](.+)/){
my $BckupKey="8-Status";
my $BckupVal="Unsuccessful";
push @{ $MyItems{$ServerName}{$BckupSet}{$BckupKey} } => $BckupVal;
print "$BckupKey=$BckupVal\n" if debug;
}
In your dump, the 8-Status
values will resemble
'8-Status' => [ 'Unsuccessful', 'Other error', 'Et cetera' ],
If you want to loop over them later, you'd do something like
foreach my $err (@{ $MyItems{$ServerName}{$BckupSet}{$BckupKey} }) {
print "got $err\n";
}
To get only the first, you'd write
print $MyItems{$ServerName}{$BckupSet}{$BckupKey}[0], "\n";
Another issue is
foreach my $ServerName(@ARGV){
while (my $line = <>){
Realize that while (<>) { ... }
implicitly loops over all files named in @ARGV
, so nesting it inside a loop over @ARGV
doesn't quite make sense. If your command line is of the form
$ readlogs server1 server2 server3 log1 log2
then you'd want to first remove from @ARGV
the servers using shift
. Distinguishing arguments the user intends as server hostnames could be tricky. One convention is using --
to signal the end of option processing, so you might
my @servers;
while (@ARGV) {
my $server = shift;
last if $server eq "--"
push @servers => $server;
}
die "Usage: $0 server .. -- log ..\n" unless @ARGV;
while (<>) {
# ...
}
This is off topic, but any time you need deeply nested data structures your code runs a risk of becoming bloated and hard to read. Simple convenience variables go a long way toward streamlining things and relieving the reader of the code (you, 3 months from now) of having to perform many mental diffs:
# A convenience var.
my $bs = $MyItems{$ServerName}{$BckupSet};
# The rest of your code can use the var.
$bs->{'1-Server'} = $ServerName;
Also, you've got several if
blocks that do basically the same thing. Seems amenable to some sort of dispatch table strategy:
my @dispatch_table = (
{
regex => qr/(backup-date)[:=](.+)/,
key => '4-DateStamp',
val => sub { $2 },
},
{
# etc.
},
);
Then your if
blocks boil down to something like this:
for my $dt (@dispatch_table){
next unless $line =~ $dt->{regex};
$bs->{ $dt->{key} } = $dt->{val}->();
}
精彩评论