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

Hi,
I have a perl file which displays the source IP, port,
Destination IP and port. I need to sort all the records displayed
and then find the uniq records.
so basically i need to apply " sort | uniq -c " to the file.
How can this be done ?


Thanks,
Himi

Replies are listed 'Best First'.
Re: Using unix commands in perl?
by McDarren (Abbot) on Oct 06, 2007 at 05:02 UTC
    - Whenever you are talking about unique "anything", almost invariably you need a hash.
    - Sorting is achieved by using the sort command.

    Post some sample data, and whatever code you already have, and you can probably get some more specific help.

    Cheers,
    Darren :)

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Using unix commands in perl?
by erroneousBollock (Curate) on Oct 06, 2007 at 05:04 UTC
    Something like this (assuming you have the records as an array of simple strings):

    my %seen; my @result = sort map { $seen{$_}++ ? () : $_ } @records;

    You may need to pass a block to sort to make the sort more specific to your records.

    If you really want to use the unix commands (say for speed), then you should investigate the use of the '|' modifier with the open built-in function.

    -David

Re: Using unix commands in perl?
by Himi (Novice) on Oct 06, 2007 at 05:24 UTC
    Hi David
    I tried the code sample you posted but
    it gives an error
    Argument "10.60.0.114" isn't numeric in ne at test.pl line 42 (#1)
    Can you please tell me how to fix this.

    Thanks,
    Himi
      Yeah, I just updated my post to reflect that :-)

      Try this:

      use warnings; use strict; use Net::IPAddr; open(file_info,$file) or die "Can't open $file "; my @records; while (<file_info>) { my ( $src_ip, $src_port ) = /IP\s+(\d+(?:\.\d+){3})\.(\d+)/; my ( $dst_ip, $dst_port ) = />\s+(\d+(?:\.\d+){3})\.(\d+)/; push @records, [$src_ip , $src_port , $dst_ip , $dst_port]; } close(file_info); my %seen; my @result = map { $_->[5] } sort { $a->[0] != $b->[0] ? $a->[0] <=> $b->[0] : $a->[1] != $b->[1] ? $a->[1] <=> $b->[1] : $a->[2] != $b->[2] ? $a->[2] <=> $b->[2] : $a->[3] <=> $b->[3] } map { $seen{$_->[4]}++ ? () : $_ } map { [ip2num($_->[0]), $_->[1], ip2num($_->[2]), $_->[3], join(',', +@$_), $_ ] } @records; print join(' ',@$_)."\n" for @results;

      [4] is the stringified record for efficient existence tests.

      [5] is the original record format for better re-use.

      -David

      Updated: made this node more stand-alone.

      A reply falls below the community's threshold of quality. You may see it by logging in.
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Using unix commands in perl?
by bruceb3 (Pilgrim) on Oct 06, 2007 at 07:56 UTC
    On your system you will never have duplicates if you are including the source IP and port, the destination IP and port because those four components must be unique on your system.

    As an example;

    If you have a service listening on a particular port and multiple incoming connections are made from the same external host, the destination IP and port will be the same (your server) and the source IP address will be the same but the source port will be different for each connection.

Re: Using unix commands in perl?
by Himi (Novice) on Oct 06, 2007 at 05:09 UTC
    Hi ,
    The code i have writtenis as follows :
    open(file_info,$file) or die "Can't open $file "; while (<file_info>) { my ( $src_ip, $src_port ) = /IP\s+(\d+(?:\.\d+){3})\.(\d+)/; my ( $dst_ip, $dst_port ) = />\s+(\d+(?:\.\d+){3})\.(\d+)/; print "$src_ip , $src_port , $dst_ip , $dst_port \n" ; } close(file_info);
    This displays the source ip, port, destination ip and port
    i need to sort these records and take the uniq ones and display them

    Himi
      Update: actually the following not going to be quite correct... need to pack the IP addresses as integers for the sort... so a more generalised (schwartzian) transform is probably better. See my next post in this thread.

      use warnings; use strict; open(file_info,$file) or die "Can't open $file "; my @records; while (<file_info>) { my ( $src_ip, $src_port ) = /IP\s+(\d+(?:\.\d+){3})\.(\d+)/; my ( $dst_ip, $dst_port ) = />\s+(\d+(?:\.\d+){3})\.(\d+)/; push @records, [$src_ip , $src_port , $dst_ip , $dst_port]; } close(file_info); my %seen; my @result = sort { $a->[0] != $b->[0] ? $a->[0] <=> $b->[0] : $a->[1] != $b->[1] ? $a->[1] <=> $b->[1] : $a->[2] != $b->[2] ? $a->[2] <=> $b->[2] : $a->[3] <=> $b->[3] } map { $seen{(join(',',@$_))}++ ? () : $_ } @records; print join(' ',@$_)."\n" for @result;

      -David

Re: Using unix commands in perl?
by Himi (Novice) on Oct 06, 2007 at 05:50 UTC
    Hi,
    Thanks David .. it works great!!
    I dont have much perl experience
    infact im a java programmer but i have been
    assigned a task that has to be done in perl and its
    very urgent so i needed help.
    Thanks so much all of you! :)

    Himi
      If you're still interested, here are a few items you may wish to look at:

      I'm pretty sure that the rest of the code I posted would be fairly familiar to most Java programmers.

      -David

Re: Using unix commands in perl?
by Prof Vince (Friar) on Oct 06, 2007 at 13:10 UTC
Re: Using unix commands in perl?
by Himi (Novice) on Oct 06, 2007 at 05:39 UTC
    Hi,
    Is there another work around for this?
    unfortunately i dont have the Net/IPAddr.pm in my perl
    package.

    Himi
Re: Using unix commands in perl?
by Cop (Initiate) on Oct 06, 2007 at 05:13 UTC

    If you are on Unixish OS, just run the command, and don't involve Perl. Always use the best tool. If you are not on Unix, just ignore this.

      If you're not on unix, install uniq and sort