use strict; use warnings; $/ = "-------------\n"; # don't bother counting dashes my %services; # keys will be service messages, values will be arrays of host IPs while (<>) # put file name on command line (or pipe data to stdin) { my @service_keys = (); my $current_service = ''; my @lines = split /\n/; # step through line-by-line... my $host = ''; while ( @lines ) { $_ = pop @lines; # ... from the bottom up next if /-------/; $current_service = "$_\n$current_service"; if ( /^Service: / ) { # top of a sub-record push @service_keys, $current_service; $current_service = ''; } elsif ( /^Host: +([\d.]+)/ ) { # top of record $host = $1; push @{$services{$_}}, $host for ( @service_keys ); last; } } warn "Record $. did not contain a host IP\n" # (update: die is too harsh here) if ( $host eq '' ); # just in case... } # %services is now HoA; let's suppose we want # to order the output by "severity" (high ... low) # but within any severity level, order doesn't matter: for my $level ( qw/High Moderate Low/ ) # any others? { for my $service ( grep /Severity: $level/, keys %services ) { print "-----------------\n\nVulnerability:\n\n$service\nHosts:\n"; print join "\n", @{$services{$service}}, "\n"; } }