mlebel has asked for the wisdom of the Perl Monks concerning the following question:

Hello everyone

I have been trying for quite some days now to find a way to get the complete opposite end results of the following script. I have read countless pages on hashes and matching but i can't figure this out...

This scripts below will get you all the matching lines within a file ( listed below as __TMPDATA__ ) matched from __DATA__ and print them out.

#!/usr/bin/perl use warnings; use strict; my $tmpdata = "tmpdata"; my $DestDevice = "Box"; my %data; open my $TMPDATA, "<", "$tmpdata" or die "$!"; while (<$TMPDATA>) { next if (/^(?:$|sh|\s*Source|^$DestDevice|Accounting)/); my @fields = split; push @{ $data{$fields[1]} }, [ @fields[0,2,3] ]; } close $TMPDATA; while (<DATA>) { chomp; for my $match (@{ $data{$_} }) { my @Results = join "\t", $match->[0], $_, @{$match}[1,2]; print @Results, "\n"; } } exit; __DATA__ 10.2.9.2 10.2.9.3 10.2.9.6 __TMPDATA__ Source Destination Packets Bytes 15.254.32.120 10.2.9.2 5 504 209.68.19.130 10.2.9.4 33 15941 17.149.36.162 10.2.9.3 14 3416 17.149.36.15 10.2.9.3 14 3404 67.148.147.65 10.2.9.5 57 39207 8.8.8.8 10.2.9.6 84 8006 Accounting data age is 0w1d Box# __ENDTMPDATA__

I am looking for the following output(non matching):

209.68.19.130 10.2.9.4 33 15941 67.148.147.65 10.2.9.5 57 39207

Any Idea's?

Thanks!

Replies are listed 'Best First'.
Re: Trying to get the complete opposite end results
by toolic (Bishop) on Feb 15, 2012 at 14:16 UTC
    You need to take the difference of the 2 arrays first, using something like Acme::Tools::minus:
    use warnings; use strict; use Acme::Tools qw(minus); my $tmpdata = "tmpdata"; my $DestDevice = "Box"; my %data; open my $TMPDATA, "<", "$tmpdata" or die "$!"; while (<$TMPDATA>) { next if (/^(?:$|sh|\s*Source|^$DestDevice|Accounting)/); my @fields = split; push @{ $data{$fields[1]} }, [ @fields[0,2,3] ]; } close $TMPDATA; my @ip1 = keys %data; my @ip2 = <DATA>; chomp @ip2; my @nomatches = minus(\@ip1, \@ip2); for (@nomatches) { for my $match (@{ $data{$_} }) { my @Results = join "\t", $match->[0], $_, @{$match}[1,2]; print @Results, "\n"; } } exit; __DATA__ 10.2.9.2 10.2.9.3 10.2.9.6
    prints:
    209.68.19.130 10.2.9.4 33 15941 67.148.147.65 10.2.9.5 57 39207
Re: Trying to get the complete opposite end results
by SuicideJunkie (Vicar) on Feb 15, 2012 at 14:07 UTC

    It looks to me that you're pushing every line all the time. The opposite of that should be a trivial blank file.

    I suggest using an if statement to decide which lines you wish to keep(print) or skip inside your while loop.

Re: Trying to get the complete opposite end results
by locked_user sundialsvc4 (Abbot) on Feb 15, 2012 at 14:37 UTC

    In a one-liner of the Bash shell that is two sort steps followed by one diff (or if you prefer, uniq).

    You don’t need to “write a program” at all.

    If you have a slew of similar operations to do, especially if you’re not always sure what you would like to do next, consider putting the data into an SQLite database (file... no server is involved) whereby you can now use LEFT OUTER JOIN in SQL to obtain non-matching rows, and nested queries to perform more complex operations.   Once again doing so from the command-line of existing tools.

    Now, of course I’m not saying that it cannot also be done in Perl ... the language is called a Swiss Army® Knife for plenty of great reasons.

Re: Trying to get the complete opposite end results
by Marshall (Canon) on Feb 15, 2012 at 20:44 UTC
    An alternate solution:

    (1)-build hash of the data
    (2)-delete the keys that are not needed
    (3)-print what is "left over"

    below I left 10.2.9.3 out of the "exclusion data" to show what would happen.

    #!/usr/bin/perl -w use strict; my $tmpdata=<<END; Source Destination Packets Bytes 15.254.32.120 10.2.9.2 5 504 209.68.19.130 10.2.9.4 33 15941 17.149.36.162 10.2.9.3 14 3416 17.149.36.15 10.2.9.3 14 3404 67.148.147.65 10.2.9.5 57 39207 8.8.8.8 10.2.9.6 84 8006 Accounting data age is 0w1d Box# END my %destination; #a hash of array # # read in all the data # open my $TMPDATA, '<', \$tmpdata or die "cannot open tmpdata $!"; while (<$TMPDATA>) { my ($ipSource, $ipDestination, $packets, $bytes) = /^\s*([\d.]+)\s+([\d.]+)\s+(\d+)\s+(\d+)\s*$/; next unless defined $bytes; push @{$destination{$ipDestination}}, [$ipSource,$packets, $bytes]; } close $TMPDATA; # # exclude (delete) some data # while (<DATA>) { next if /^\s*$/; #skip blanks chomp; delete $destination{$_} if exists $destination{$_}; } # # print what is left over # foreach my $dest ( sort keys %destination) { foreach my $source_ref (@{$destination{$dest}}) { printf "%s\t$dest\t%s\t%s\n", @$source_ref; } } =prints 17.149.36.162 10.2.9.3 14 3416 17.149.36.15 10.2.9.3 14 3404 209.68.19.130 10.2.9.4 33 15941 67.148.147.65 10.2.9.5 57 39207 =cut __DATA__ 10.2.9.2 10.2.9.6
Re: Trying to get the complete opposite end results
by jwkrahn (Abbot) on Feb 15, 2012 at 21:13 UTC
    #!/usr/bin/perl use warnings; use strict; my $tmpdata = 'tmpdata'; my $DestDevice = 'Box'; my %data; while ( <DATA> ) { chomp; $data{ $_ } = 1; } open my $TMPDATA, '<', $tmpdata or die "Cannot open '$tmpdata' because +: $!"; while ( <$TMPDATA> ) { next if /^(?:$|sh|\s*Source|$DestDevice|Accounting)/; my @fields = split; next if $data{ $fields[ 1 ] }; print join( "\t", @fields ), "\n"; } close $TMPDATA; exit 0; __DATA__ 10.2.9.2 10.2.9.3 10.2.9.6
Re: Trying to get the complete opposite end results
by mlebel (Hermit) on Feb 16, 2012 at 12:40 UTC

    Thank you all for your really helpful and diverse solutions.

    I now have a proper working solution in place inside my script where this code fits in and I can finally finish it.

    Thanks again!