Help with Hash of Hashes

S

sunckell

Hello everyone,
I am working on a script that parses a maillog (sendmail). I am
trying to gather information about particular messages from the log.
Namely the "to", "status", "date & time" , and the "msgid" from the
Exchange server that is sending the email through the sendmail server.

Since the information I need is on separate log lines, I thought I
would first read the log file for the particular "to" addresses (3
different ones) and grab the ID sendmail assigns to it, then process
the log again and add the information to a hash of hashes based on the
sendmail ID.

So in my head I figured my data structure would look something like
this:

# a hash which holds a hash of information for each sendmail ID I
grabbed from the log on the #first scan.


my %HoH = (
"$msgid" => {
'date' => "$date",
'year' => "$year",
'time' => "$time",
'exid' => "$exid",
'status' => "$status",
'email' => "$eMail"
);

I then figured I could add hashes and key value pairs while processing
each line of the log file.

I thought it all sounded good, until I tried to write it. I have a
subroutine that processes the log file. When I am done processing the
file, and I believe the hash of hashes is populated, I try to print out
the information, but I only get the last entry "infomation" that I was
able to get from the file. The hash of hashes doesn't grow as I find
more sendmail ID's and it doesn't add key value pairs as I find them.

If anyone could point me in the right direction I would be most
appreciated.

Here is the code:

---------------------------------------------------------------------
sub gather_message_info{
# @smids are the sendmail id's I gathered in a previous pass thru
the logs.
my($log, $month, $day, $hour, @smids) = @_;

my $mail_ids = join("|", @smids);

my %HoH; #initialize my hash of hashes

open(LOG,"$log") || die "Can't open $log: $! \n";
while(<LOG>){

#only want the past hours worth of emails for specified msgids
only.
next if(!/^$month\s+$day\s$hour/);
next if(!/$mail_ids/);

/^
(\S{3})\s+(\d+) # date -- 1, 2
\s
(\d+):(\d+):(\d+) # time -- 3, 4, 5
\s
(?:\S{4}) # hostname
\s
(?:\w+\[\d+\]:) # service name and process ID
\s
(?:\[.{15,}?\]) # syslog ID and level
\s
(.{13,}?:) # message ID
\s
(.*) # text -- 7

$/x or do
{
my $warn = "\t$_\n";
push(@warnings,$warn);
};

my $date = "$1 $2";
my $time = "$3:$4:$5";
my $hour = "$3";
my $msgid = "$6";
my $text = "$7";

%HoH = (
"$msgid" => {
"date" => "$date",
"year" => "$year",
"time" => "$time",
}
);

#add exchange id to the hash.
if($text =~ /msgid\=</){
my($ex_id) = (split /\s+/, $text)[4];

#clean up the output a little
$ex_id =~ s/msgid\=<//;
$ex_id =~ s/>,//;

$HoH{$msgid}{exid} = $ex_id;

}

if($text =~ /^to\=</){

#check status first.
my $status = (split /stat\=/, $text)[1];
if ($status =~ /Sent\s\(OK\)/){

$HoH{$msgid}{status} = $status;

}else{

next;

}

#grab the email address
my($eMail) = (split /\s+/, $text)[0];

#clean up the output a little
$eMail =~ s/to\=<//;
$eMail =~ s/>,//;

$HoH{$msgid}{email} = $eMail;

}

}

close(LOG);

return(%HoH);

}


Now if I print the %HoH with Data::Dumper like so:

print Dumper \%HoH;


All I get is:

$VAR1 = {
'k21HwINE006701:' => {
'email' => '(e-mail address removed)',
'year' => 2006,
'status' => 'Sent (OK)',
'date' => 'Mar 1',
'time' => '12:58:32'
}
};

When I know I should have 10 message ids (hashes within the main hash)
with all the key value pairs filled (email, year,
status,date,time,exid, etc).

Once again thanks for the guidance.

Sincerely,
sunckell
 
P

Paul Lalli

sunckell said:
my %HoH = (
"$msgid" => {
'date' => "$date",
'year' => "$year",
'time' => "$time",
'exid' => "$exid",
'status' => "$status",
'email' => "$eMail"
);

Please read:
perldoc -q quoting
What's wrong with always quoting "$vars"?

my %HoH = (
$msgid => {
date => $date,
year => $year,
time => $time,
exid => $exid,
status => $status,
email => $eMail,
}
);

I thought it all sounded good, until I tried to write it. I have a
subroutine that processes the log file. When I am done processing the
file, and I believe the hash of hashes is populated, I try to print out
the information, but I only get the last entry "infomation" that I was
able to get from the file. The hash of hashes doesn't grow as I find
more sendmail ID's and it doesn't add key value pairs as I find them.

sub gather_message_info{
# @smids are the sendmail id's I gathered in a previous pass thru
the logs.
my($log, $month, $day, $hour, @smids) = @_;

my $mail_ids = join("|", @smids);

my %HoH; #initialize my hash of hashes

You declare one big Hash in this subroutine.
open(LOG,"$log") || die "Can't open $log: $! \n";

1) Use the three argument form of open
2) Use Lexical filehandles, not global barewords
3) Again, perldoc -q quoting

open my $LOG, '<', $log or die "Cannot open '$log': $!\n";
while(<LOG>){
my $date = "$1 $2";
my $time = "$3:$4:$5";
my $hour = "$3";
my $msgid = "$6";
my $text = "$7";

perldoc -q quoting
%HoH = (
"$msgid" => {
"date" => "$date",
"year" => "$year",
"time" => "$time",
}
);

Here you are *reassigning* %HoH to be a completely new hash. You are
not adding any elements to the existing hash. This is analagous to:

my %hash;
while ($foo = do_stuff()){
%hash = ($foo => $bar);
}

whereas you meant to do:
while ($foo = do_stuff()){
$hash{$foo} = $bar;
}

So in this case, you simply want to assign one more key/value pair to
the existing hash:

$HoH{$msgid} = {
date => $date,
time => $time,
year => $year,
};


Paul Lalli
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Deleting duplicate values in hash of hashes 1
Help with code 0
hash of hashes 9
Hash of Hashes 5
Sorting hash of hashes 3
Sorting hash of hashes 9
Help with my responsive home page 2
Need help with this script 4

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,072
Latest member
trafficcone

Latest Threads

Top