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

I have written a function to get a list of ip v6 addresses from a db using sql query. The function works as expected and returns the ip addresses. The output contains the ipv6 addresses. Now, i want to get rid of ::ffff: from all the ip addresses. I want use the following regex /(\d+.\d+.\d+.\d+)/ in the below function to get only ip address in the output. I'm not really sure how to integrate the regex match with the below function.

sub subroutine { my ($self,$vip) = @_; my @ip = (); my $sql_query = $self->{queryObj}->execute( "select machineIP from 'tablename' where frontend=$vip"); my $records = $self->{queryObj}->result(); foreach my $row (@$records) { push @ip, $row->{machineIP}; } return \@ip; }

Function call:

$self->{'get_ip'} = $self->{'queryObj'}->subroutine( 'x.x.x.x' );

output :

::ffff:172.81.139.17

::ffff:198.81.139.21

::ffff:198.81.139.19

Replies are listed 'Best First'.
Re: regex in perl
by stevieb (Canon) on Jun 15, 2016 at 00:24 UTC

    Untested. It'll push the IPv6 addresses onto @ip, but if there's a v4 address in an entry, it'll grab just the four v4 octets and will push only the x.x.x.x:

    for my $row (@$records){ if (my ($ip) = $row->{machineIP} =~ /(\d+\.\d+\.\d+\.\d+)/){ $row->{machineIP} = $ip; } push @ip, $row->{machineIP}; }

    If a $row->{machineIP} matches the regex, we capture just the x.x.x.x portion with the () capture group (ignoring everything else in the string), and assigns it to $ip. We need parens around $ip because if we didn't have them, we'd be assigned to in scalar context, and would get the count of matches. () forces list context, so we get the actual matches, not just the count.

    If there's no match, we just push the db row entry onto @ip as-is.

      This doesn't work
        can you prove it? rewrite sub subroutine so it starts with  my $records = ...; # Data::Dumper output here
Re: regex in perl
by BillKSmith (Monsignor) on Jun 15, 2016 at 04:22 UTC
    The safest way to match an ipv6 address is to use the module Regexp::Common::net. Use a second regex to select the ones you want. I do not exactly understand your requirements, but this should get you started.
    use Regexp::Common qw /net/; ...; my @temp = grep {$RE{net}{IPv6}{-sep=>':'}{-style=>'HeX'}} @$records; my @ip = grep {!/^[a-f0-9]{4}\:[a-f0-9]{4}\:ffff/i} @temp;

    UPDATE:

    The original syntax is correct, but AnomalousMonks' suggestion is probably clearer.

    Soonix's requirement can be met by specifying a regex for sep. It probably will not extract the ipv4 address correctly, but that is not a requirement in this case.

    {-sep=>qr/[:.]/}
    Bill
      my @temp = grep {$RE{net}{IPv6}{-sep=>':'}{-style=>'HeX'}} @$records;

      I haven't tested it, but I think the  $RE{net}{IPv6}{-sep=>':'}{-style=>'HeX'} expression is just a Regexp object and needs, in this case, to be interpolated into an  m// operator:
          my @temp = grep { m/$RE{net}{IPv6}{-sep=>':'}{-style=>'HeX'}/ } @$records;
      Or slightly more simply:
          my @temp = grep m/$RE{net}{IPv6}{-sep=>':'}{-style=>'HeX'}/, @$records;
      In the second case, note the  m// is delimited by a  ,(comma).

      Update: Or you could explicitly bind to the "topicalization" scalar  $_ with the  ~= operator (again, untested):
          my @temp = grep { $_ =~ $RE{net}{IPv6}{-sep=>':'}{-style=>'HeX'} } @$records;
      or:
          my @temp = grep $_ =~ $RE{net}{IPv6}{-sep=>':'}{-style=>'HeX'}, @$records;


      Give a man a fish:  <%-{-{-{-<

      The original syntax is correct ...

      The original syntax may be correct in that it compiles without error, but it is semantically incorrect in that it doesn't actually do anything. grep looks at the truthiness of the evaluated expression or statement block, and a Regexp object alone is always true. Only when evaluated within a match operator can it result in a true or false value. Consider:

      c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "use Regexp::Common qw /net/; ;; my $records = [ qw(1.2.3.4 12.23.34.45 9.9.9 x.x.x.x anything) ]; ;; my @temp = grep {$RE{net}{IPv4}} @$records; dd \@temp; ;; @temp = grep { m/$RE{net}{IPv4}/ } @$records; dd \@temp; " ["1.2.3.4", "12.23.34.45", "9.9.9", "x.x.x.x", "anything"] ["1.2.3.4", "12.23.34.45"]

      Update: The code would also have been semantically correct had it used an explicit binding of  $_ to the Regexp object:

      c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "use Regexp::Common qw /net/; ;; my $records = [ qw(1.2.3.4 12.23.34.45 9.9.9 x.x.x.x anything) ]; ;; my @temp = grep { $_ =~ $RE{net}{IPv4} } @$records; dd \@temp; " ["1.2.3.4", "12.23.34.45"]


      Give a man a fish:  <%-{-{-{-<

        Thanks for the explanation! I am must check for the analogous error in other projects.
        Bill
      My guess (from looking at the source) is, however, that Regexp::Common::net doesn't (yet?) know about the mixed "alternate form" with colons and dots.
Re: regex in perl
by Anonymous Monk on Jun 15, 2016 at 00:19 UTC