use strict; use Data::Dumper; my %backups; while () { next if (/^\s*$/); #skip blank lines chomp; my ($date, $backupset , $parm , $value) = parseline($_); if ($value) { $backups{$backupset}{$parm} = $value; } } print Dumper \%backups; sub parseline { my $line = shift; my ($date, $rest) = $line =~ m/(^.*\d{4}):(.*)/; my ($backupset, $msg) = split(/backup:INFO:/, $rest); $backupset =~ s/:\s*$//; #trim some unwanted thing like ':' is ok $backupset =~ s/^\s*backup\.//; #more than one step is just fine! my ($parm, $value) = $msg =~ m/\s*(.*)=\s*(.*)\s*/; $parm ||= $msg; #if match doesn't happen these will be undef $value ||=""; #this trick makes sure that they are defined. return ($date, $backupset, $parm, $value); } =print #some reformatting to try to stop line wrap.... $VAR1 = { 'set1_lvm' => { 'backup-size' => '187.24 GB', 'backup-set' => 'backup.set1_lvm', 'backup-time' => '01:59:04', 'backup-date-epoch' => '1281942003', 'backup-status' => 'Backup succeeded', 'last-backup' => '/home/backups/backup.set1_lvm/20100815000006', 'backup-type' => 'regular', 'backup-date' => '20100816000003' }, 'set2_lvm_lvm' => { 'backup-size' => '424.53 GB', 'backup-time' => '04:33:12', 'backup-status' => 'Backup succeeded', 'last-backup' => '/home/backups/backup.set2_lvm_lvm/20100814200003' }, 'set2_lvm' => { 'backup-directory' => '/home/backups/backup.set2_lvm/20100815200003', 'backup-set' => 'backup.set2_lvm', 'backup-date-epoch' => '1281927603', 'backup-type' => 'regular', 'backup-date' => '20100815200003' } }; =cut __DATA__ Sun Aug 15 20:00:03 2010: backup.set2_lvm:backup:INFO: START OF BACKUP Sun Aug 15 20:00:04 2010: backup.set2_lvm:backup:INFO: backup-set=backup.set2_lvm Sun Aug 15 20:00:04 2010: backup.set2_lvm:backup:INFO: backup-date=20100815200003 ..... use __DATA__ segment from my previous post