How to extract all IP addresses present in a line from a file?

Viewed 99

I have a file that contains some info including IP addresses like below (a line may contains more than one IP):

ip_group1,1.2.3.4,otherstring1,otherstring2,4.5.6.7
ip_group2,3.4.5.6,otherstring1
ip_group3,11.21.31.41,otherstring1,otherstring2,4.5.6.7,otherstring4,1.2.3.4,otherstring5,otherstring2,41.51.16.71

I am using this portion of the Perl code to extract the IPs, but it only extracts the first occurrence of the IP and leaves the other IPs present in the line and process the next.

use Regexp::Common qw/net/;
while (<>) {
  print $1, "\n" if /($RE{net}{IPv4})/;
}

How to get all the IPs present in a line?

My expected output is below:

ip_group1,1.2.3.4,4.5.6.7
ip_group2,3.4.5.6
ip_group3,11.21.31.41,4.5.6.7,1.2.3.4,41.51.16.71
2 Answers

If you match globally (//g) in a while loop, you can get all IP addresses:

use warnings;
use strict;
use Regexp::Common qw/net/;

while (<DATA>) {
    my ($grp) = /^(\w+)/;
    my @ips;
    while (/($RE{net}{IPv4})/g) {
        push @ips, $1; 
    }
    print join(',', $grp, @ips), "\n";
}
__DATA__
ip_group1,1.2.3.4,otherstring1,otherstring2,4.5.6.7
ip_group2,3.4.5.6,otherstring1
ip_group3,11.21.31.41,otherstring1,otherstring2,4.5.6.7,otherstring4,1.2.3.4,otherstring5,otherstring2,41.51.16.71

Prints:

ip_group1,1.2.3.4,4.5.6.7
ip_group2,3.4.5.6
ip_group3,11.21.31.41,4.5.6.7,1.2.3.4,41.51.16.71

With a global match (/g) in a list context all matches are returned (see it in perlretut)

while (<>) {
    my @ips_line = /($RE{net}{IPv4})/g;
    ...
}

Altogether, one way

use warnings;
use strict;
use Regexp::Common qw/net/;

my @ips;

while (<>) {
    my ($group) = /^([^,]+)/;        
    my @ips_line = /($RE{net}{IPv4})/g;
    push @ips, join ',', $group, @ips_line;
}

say for @ips;

Or, since join imposes list context on its second (LIST) operand†

while (<>) {
    push @ips, join ",", /^([^,]+)/, /($RE{net}{IPv4})/g 
}

Or, another way

while (<>) {
    chomp;
    my ($group, $terms) = split /,/;
    push @ips, join ',', $group, grep { /$RE{net}{IPv4}/ } @terms;
}

In all this, there are various edge cases I can think of (missing group word, lines without any IPs, lines with other text altogether ...). Let us know if any (and which) can happen and how they should be handled.


† See Description in perlfunc for discussion of Perl's operators and context

Related