Marcin K
Marcin K

Reputation: 37

Perl regex one line file

I'm using cat to get content of the file, and then I use regex to get what I want. When I change order of if's in example i put PASSWORD as a second one, IP will be empty.

#!/usr/bin/perl
my $param = $ARGV[0];

my $IP;
my $PASSWORD;
my $MODEM;
my @cat = `cat file`;

 foreach my $line (@cat){
    if ($line =~ /$param/){
        if ($line =~ /MODEM:(\w+)/g){ $MODEM = $1; }
        if ($line =~ /IP:((\d{1,3}\.){3}\d{1,3})/g){ $IP = $1; }
        if ($line =~ /PASSWORD:(.+)/g){ $PASSWORD = $1; }

        print $MODEM;
        print $IP;
        print $PASSWORD;

        }
}

The content of a file (data is changed to fake values) :

MODEM:SOMETHING ID:1000018 MAC:606060606060  ID2:123499  IP:1.1.1.1  PASSWORD:pass123

Expected result is:

SOMETHING1.1.1.1pass123 

But when I change the line of code to this:

if ($line =~ /MODEM:(\w+)/g){ $MODEM = $1; }
if ($line =~ /PASSWORD:(.+)/g){ $PASSWORD = $1; }
if ($line =~ /IP:((\d{1,3}\.){3}\d{1,3})/g){ $IP = $1; }

the result is :

SOMETHINGpass123 

i.e. the IP is empty. Can someone explain me why this is happening?

Upvotes: 2

Views: 257

Answers (2)

Dada
Dada

Reputation: 6626

Edit: you probably don't really want to use 3 complicated regex to parse your file. See Stefan Becker's answer for a better way.

You need to remove the /g modifier.

Successive matches against the same string with the /g start from the end of the previous match. Have a look at the relevant paragraph in perlretut.
In your file, MODEM comes first, then IP, then PASSWORD. Which means that after matching /PASSWORD:(.+)/g, you can't match /IP/g which comes before in your string.


/g modifier is mostly useful when you want to do something like

while ($string =~ /.../g) { ... }

Or of course, in list context, to get all the matches:

 @matches = $string =~ /.../g

In your case, you just want to get different matches one by one, so no need for /g modifier.

Upvotes: 7

Stefan Becker
Stefan Becker

Reputation: 5952

Maybe not the answer you were looking for, but IMHO for your structured data a simpler approach than regex is possible:

  • split at white space to access the fields
  • each field is of the form <KEY>:<VALUE, i.e. can be converted to a hash
#!/usr/bin/perl
use warnings;
use strict;

while (<DATA>) {
    chomp;
    my %fields = map { split(/:/) } split(' ');

    print "$fields{MODEM} $fields{IP} $fields{PASSWORD}\n"
}

exit 0;

__DATA__
MODEM:SOMETHING ID:1000018 MAC:606060606060  ID2:123499  IP:1.1.1.1  PASSWORD:pass123

Another alternative would be

my %fields = /(\S+):(\S+)/g;

Test run:

$ perl dummy.pl
SOMETHING 1.1.1.1 pass123

Upvotes: 7

Related Questions