简体   繁体   中英

In Perl, how can I store arrays as values in a hash?

I don't know if you're allowed to ask questions about a school assignment. I just want to understand what I am supposed to do, not do this for me. Maybe I am missing something so simple it is right in front of me but anyways it was based off of an older assignment, but I had missed this class and I am running into a wall now, and well the problem is that I'm trying to push an array into a hash table. using something like this;

push @{$hash_table{$hash_key}}, $port

and to count the ports while storing them and then to print the contents of the hash

while ( ($key , $value) = each ( %hash ) ) {
    print “$key scanned @{$value}” 
}

and if I wanted to sort the results I would use

< foreach $key ( keys ( %hash ) ) {
}

The current code I have is this, searching for the string of /iNext-DROP/ using a log file provided. I cant for the life of me find the correct place to add the code above

use warnings;
my $LogRecord; 
my $LogRecordCount;
open LOGFILE, "sample.log.txt" or die "couldn't open sample.log.txt";
while ($LogRecord = <LOGFILE>) {
if  ($LogRecord =~ /INext-DROP/)    { 
    $LogRecordCount ++;
    $LogRecord =~ /(SRC=[0-9\.]* ).*(SPT=[0-9\.]* )/;
    $source=$1;
    $sport=$2;
print "$source$sport"; 
print substr( $LogRecord , 0 , $ARGV[1] ) , "\n"  if $ARGV[1];
 }
}
print "The file contained $LogRecordCount records" if $ARGV[1];
close LOGFILE;

Here's a picture of the old code with comments ; Old Code -- not much has changed since I keep going back after it doesn't work

The thing you're seemingly having problems with, is that you're not sure where the port is being captured and the hash update.

What's happening is your while loop is iterating the file one line at a time, and capturing values - that $LogRecord =~ line is capturing a pattern - into $1 and $2 .

And then that $2 is the thing you can add to your hash, with push .

However, there's a few things I have changed style wise like using lexical file handles because it's better style.

#!/usr/bin/env perl
use warnings;
use strict; 

#because it makes debugging easier. 
use Data::Dumper;

my $LogRecordCount;

#declare some hashes; 
my %ports_from; 
my %ips_that_used; 

open my $logfile, "sample.log.txt" or die "couldn't open sample.log.txt";
while (my $line = <$logfile>) {
    #matches 'current line' - skips stuff that doesn't match. 
    next unless $line =~ /INext-DROP/; 
    #increment count. 
    $LogRecordCount++;

    my ( $source, $src_port ) = $line =~ m/SRC=([0-9\.]+).*SPT=([0-9]+)/;  
    print "$source$sport"; 

    #not sure what this is doing, so I have left it in. 
    print substr( $line , 0 , $ARGV[1] ) , "\n"  if $ARGV[1];

    push @{$ports_from{$source}}, $src_port; 
    push @{$ips_that_used{$src_port}}, $source; 
 }
print "The file contained $LogRecordCount records" if $ARGV[1];
close $logfile;

print Dumper \%ports_from;
print Dumper \%ips_that_used; 

That's built up your hashes.

But when it comes to outputing:

foreach my $ip ( keys %ports_from ) { 
  print "$ip: ", join ( " ", @{$ports_from{$ip}}) ,"\n"
}

If you wanted to sort them, you would have to do this using sort .

Now sort is a pretty clever function, but by default is sorts alphanumerically. That's ... actually not all that useful when it comes to IP addresses or port numbers, because you probably want to sort those more numerically. The easy answer is Sort::Naturally and use nsort .

However - sort takes a function (it defaults to cmp ) that returns -1, 0, 1 depending on relative position.

So sorting 'by IP' might look like this:

sub by_ip { 
   my @a = split /\./, $a;
   my @b = split /\./, $b; 
   foreach my $octet ( @a ) { 
      my $comparison = $octet <=> shift ( @b ); 
      return $comparison if $comparison; 
   }
   return 0;
}

And then you could:

foreach my $ip ( sort by_ip keys %ports_from ) { 
   print "$ip: ", join ( " ", sort { $a <=> $b } @{$ports_from{$ip}}),"\n";
}

Giving you:

24.64.208.134 : 24128  24128  24128 
71.228.199.109 : 37091 
72.197.8.56 : 9258 
75.117.31.43 : 3122 
99.248.20.48 : 48725 
207.68.178.56 : 80 

Given an IP-to-port mapping where duplicates exist though, it might be better still to merely count port-frequency using a hash-of-hashes instead of a hash-of-arrays.

$count_ports_from{$source}{$src_port}++; 

And then:

foreach my $ip ( sort by_ip keys %count_ports_from ) {
   print "$ip: ";
   foreach my $port_num ( sort { $count_ports_from{$a} <=> $count_ports_from{$b} } 
       keys %{ $count_ports_from{$ip} } )
   {
      print "\t $port_num : $count_ports_from{$ip}{$port_num}\n";
   }
}

Giving you something like:

24.64.208.134 :      24128  : 3
71.228.199.109 :     37091  : 1
72.197.8.56 :    9258  : 1
75.117.31.43 :   3122  : 1
99.248.20.48 :   48725  : 1
207.68.178.56 :      80  : 1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM