views:

76

answers:

4

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' => {
                                  '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
A: 

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";
}
Cfreak
@Cfreak - thanks for your help, but no output :-(
jda6one9
Do you get an error? I tested it and I get the expected output. You should use that in conjunction with your existing code. Sorry if that wasn't clear. Replace the Dumper() call with what I have.
Cfreak
@Cfreak - Yes, error. I even tried just printing of one them and no luck.ErrorMesg:Bareword "backup" not allowed while "strict subs" in use at ./hashtst2.pl line 56.Bareword "backup" not allowed while "strict subs" in use at ./hashtst2.pl line 56.Bareword "status" not allowed while "strict subs" in use at ./hashtst2.pl line 56.I edited the code to show entire script including the variables.
jda6one9
just quote `backup-time` and `backup-status`.
Pedro Silva
@Pedro - thanks, I quoted it. No errors now, but still no output :-(Is my hash correct? It must be if dumper is showing data.
jda6one9
what version of perl are you using?
Cfreak
I am using version 5.8
jda6one9
+1  A: 

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

}
daotoad
@daotoad - Thanks for your input. No, no reason other than suggestions I picked up from this forum. I'm new to programming and using perl. So best practices for me would be helpful.
jda6one9
@daotoad - I added a sample of the log file in my question that I am going thru to collect my dataset. Its using the regex to populate the hash. Being a newbie to perl, its confusing to me as to which is better to use (array or hash, hash of hashes). Personally, a clean visual representation helps. If you can provide a best approach for my skill level that I can use would be appreciated. thanks!!
jda6one9
@daotoad - unfortunately, i'm running perl 5.8 :-(Can't locate feature.pm
jda6one9
`feature` is part of Perl 5.10 plus.
daotoad
@daotoad- thank you very much for your input. I am trying to learn this language and will heed your advice. Yes, I was using this statement to print the dumper: 'print Dumper(%MyItems);'Thank you for your bolted solution. I will test it out and let you know how it goes.
jda6one9
A: 

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.

Paul Hutchinson
A: 

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