help printing out hash keys to needed format
I need help printing out data from a hash/hash ref to STDOUT or file with data in a specific order if possible.
I have a perl routine that uses hash references like so:
#!/usr/local/bin/perl
use strict;
use warnings;
use File::Basename;
use Data::Dumper;
my %MyItems;
my $ARGV ="/var/logdir/server1.log";
my $mon = 'Aug';
my $day = '06';
my $year = '2010';
while (my $line = <>)
{
chomp $line;
if ($line =~ m/(.* $mon $day) \d{2}:\d{2}:\d{2} $year: ([^:]+):backup:/)
{
my $server = basename $ARGV, '.log';
my $BckupDate="$1 $year";
my $BckupSet =$2;
$MyItems{$server}{$BckupSet}->{'MyLogdate'} = $BckupDate;
$MyItems{$server}{$BckupSet}->{'MyDataset'} = $BckupSet;
$MyItems{$server}{$BckupSet}->{'MyHost'} = $server;
if ($line =~ m/(ERROR|backup-size|backup-time|backup-status)[:=](.+)/)
{
my $BckupKey=$1;
my $BckupVal=$2;
$MyItems{$server}{$BckupSet}->{$BckupKey} = $BckupVal;
}
}
}
foreach( values %MyItems ) {
print "MyHost=>$_->{MyHost};MyLogdate=>$_->{MyLogdate};MyDataset=>$_->{MyDataset};'backup-time'=>$_->{'backup-time'};'backup-status'=>$_->{'backup-status'}\n";
}
Output using dumper:
$VAR1 = 'server1';
$VAR2 = {
'abc1.mil.mad' => {
'ERROR' => ' If you are sure is not running, please remove the file and restart ',
'MyLogdate' => 'Fri Aug 06 2010',
'MyHost' => 'server1',
'MyDataset' => 'abc1.mil.mad'
},
'abc2.cfl.mil.mad' =&开发者_运维百科gt; {
'backup-size' => '187.24 GB',
'MyLogdate' => 'Fri Aug 06 2010',
'MyHost' => 'server1',
'backup-status' => 'Backup succeeded',
'backup-time' => '01:54:27',
'MyDataset' => 'abc2.cfl.mil.mad'
},
'abc4.mad_lvm' => {
'backup-size' => '422.99 GB',
'MyLogdate' => 'Fri Aug 06 2010',
'MyHost' => 'server1',
'backup-status' => 'Backup succeeded',
'backup-time' => '04:48:50',
'MyDataset' => 'abc4.mad_lvm'
}
};
Output formatted that I would like to see:
MyHost=>server1;MyLogdate=>Fri Aug 06 2010;MyDataset=>abc2.cfl.mil.mad;backup-time=>Fri Aug 06 2010;backup-status=>Backup succeeded
Just addded (8/7/2010): Sample raw log file I am using: (recently added to provide better representation of the source log)
Fri Aug 06 00:00:05 2010: abc2.cfl.mil.mad:backup:INFO: backup-set=abc2.cfl.mil.mad
Fri Aug 06 00:00:05 2010: abc2.cfl.mil.mad:backup:INFO: backup-date=20100806000004
Fri Aug 06 00:48:54 2010: abc4.mad_lvm:backup:INFO: backup-size=422.99 GB
Fri Aug 06 00:48:54 2010: abc4.mad_lvm:backup:INFO: PHASE END: Calculating backup size & checksums
Fri Aug 06 00:48:54 2010: abc4.mad_lvm:backup:INFO: backup-time=04:48:50
Fri Aug 06 00:48:54 2010: abc4.mad_lvm:backup:INFO: backup-status=Backup succeeded
Fri Aug 06 00:48:54 2010: abc4.mad_lvm:backup:INFO: Backup succeeded
I've spent some time looking at your code and I think I have it figured out.
The reason this was hard to answer is that you've unintentionally planted a red herring--the data dumper output.
Notice how it shows $VAR1 = 'server1';
and then $VAR2 = { blah };
.
You called Dumper like so: print Dumper %MyItems;
The problem is that Dumper wants a list of values to dump, since Perl flattens lists, complex structures must be passed by reference. So, you need to call Dumper like so:
print Dumper \%MyItems;
This shows the whole structure.
When you called dumper earlier, you inadvertently stripped off one layer of your data structure. The proposed solutions, and your own code are operating on this stripped structure.
Here I've bolted on some code to handle additional layer of nesting (and made it Perl 5.8 compatible):
for my $server_items ( values %MyItems ) {
for my $record ( values %$server_items ) {
print join ';', map {
# Replace non-existant values with 'undef'
my $val = exists $record->{$_} ? $record->{$_} : 'undef';
"'$_'=>$val" # <-- this is what we print for each field
} qw( MyHost MyLogdate MyDataset backup-time backup-status );
print "\n";
}
}
It looks like you have a lot of questions and need some help getting your head around a number of concepts. I suggest that you post a request on Perlmonks in Seekers of Perl Wisdom for help improving your code. SO is great for focussed question, but PM is more amenable to code rework.
** Original answer: **
To get around any parsing issues that I can't replicate, I just set %MyItems
to the output of Dumper you provided.
Your warnings you mention above have to do with all the complicated quoting and repetitive coding you have in your print statement. I have replaced your print statement with a map
to simplify the code.
Holy crap, a big join map blah is not simpler, you might be thinking. But really, it is simpler because each individual unit of expression is smaller. What is easier to understand and get right? What is easier to alter and maintain in a correct and consistent manor?
print "'foo'=>$_->{foo};'bar'=>$_->{bar};boo'=>$_->{boo};'far'=>$_->{far}\n";
or
say join ';', map {
"'$_'=>$item->{$_}"
} qw( foo bar boo far );
Here, you can add, remove or rearrange your output merely by changing the list of arguments passed to map
. With the other style, you've got a bunch of copy/paste to do.
The map I use below is a bit more complex, in that it checks to see if a given key is defined before printing a value, and assign a default value if none is present.
#!perl
use strict;
use warnings;
use feature 'say';
my %MyItems = (
'abc1.mil.mad' => {
'ERROR' => ' If you are sure is not running, please remove the file and restart ',
'MyLogdate' => 'Fri Aug 06 2010',
'MyHost' => 'server1',
'MyDataset' => 'abc1.mil.mad'
},
'abc2.cfl.mil.mad' => {
'backup-size' => '187.24 GB',
'MyLogdate' => 'Fri Aug 06 2010',
'MyHost' => 'server1',
'backup-status' => 'Backup succeeded',
'backup-time' => '01:54:27',
'MyDataset' => 'abc2.cfl.mil.mad'
},
'abc3.mil.mad' => {
'backup-size' => '46.07 GB',
'MyLogdate' => 'Fri Aug 06 2010',
'MyHost' => 'server1',
'backup-status' => 'Backup succeeded',
'backup-time' => '00:41:06',
'MyDataset' => 'abc3.mil.mad'
},
'abc4.mad_lvm' => {
'backup-size' => '422.99 GB',
'MyLogdate' => 'Fri Aug 06 2010',
'MyHost' => 'server1',
'backup-status' => 'Backup succeeded',
'backup-time' => '04:48:50',
'MyDataset' => 'abc4.mad_lvm'
}
);
for my $record ( values %MyItems ) {
say join ';', map {
my $val = $record->{$_} // 'undef'; # defined-or requires perl 5.10 or newer.
"'$_'=>$val" # <-- this is what we print for each field
} qw( MyHost MyLogdate MyDataset backup-time backup-status );
}
Not tested but it should work in theory. This will print an output line for each of the keys for the main MyItems hash. If you want it all on one line you can just drop the \n or add some other separator.
foreach( values %MyItems ) {
print "MyServer=>$_->{MyServer};MyLogdate=>$_->{MyLogdate};MyDataset=>$_->{MyDataset};backup-time=>$_->{backup-time};backup-status=>$_->{backup-status}\n";
}
Not answering the question you asked, but this doesn't seem sensible to me.
You want an array of hashes not a hash of hashes.
Hashes are not ordered, if you want them ordered then use an array.
Thanks everyone for pitching in their help... This works for me.
for my $Server(keys%MyItems){
for my $BckupSet(keys%{$MyItems{$Server}}){
for(sort keys%{$MyItems{$Server}{$BckupSet}}){
print$_,'=>',$MyItems{$Server}{$BckupSet}{$_},';';
}
print"\n";
}
}
精彩评论