E.T.
E.T.

Reputation: 31

Why is Perl not printing all regex matches found in single line?

I have a regex that matches multiple Strings inside a line in a text file. However, when I use it to try to print all the instances of the captured group it is only printing the first instance.

My regex is:

/"resolution.(\w+)/g

When feeding the following line to the regex:

"signalcfg": "{\"signals\":[{\"order\":1,\"id\":\"oryx_C20C0E15-2028-4F4B-A8DD-0DA8D87B4FF9\",\"name\":\"success\",\"rrcodes\":\"resolution.COMPLAINTS_CHANGE_STATUS_SUCCESS\",\"testModule\":\"MCSChangeComplaintsStatus\",\"default\":false},{\"order\":2,\"id\":\"oryx_C943ADB8-6FA2-4DA1-B4D7-24515D96B9DA\",\"name\":\"TimeOut\",\"rrcodes\":\"resolution.MCS_CHANGE_COMPLAINTS_STATUS_TIMEOUT\",\"testModule\":\"MCSChangeComplaintsStatus\",\"default\":false},{\"order\":3,\"id\":\"oryx_0CAC0F97-AD57-4C49-807A-B41839191F74\",\"name\":\"Warning\",\"rrcodes\":\"resolution.MCS_CHANGE_COMPLAINTS_STATUS_WARNING\",\"testModule\":\"MCSChangeComplaintsStatus\",\"default\":false},{\"order\":4,\"id\":\"oryx_4583A3EC-DFC8-47B9-9B04-DEE71DC3F17A\",\"name\":\"APIError\",\"rrcodes\":\"resolution.COMPLAINTS_CHANGE_STATUS_FAIL,resolution.MCS_CHANGE_COMPLAINTS_STATUS_FAIL\",\"testModule\":\"MCSChangeComplaintsStatus\",\"default\":true}]}",

It matches all expressions like this:

"resolution.COMPLAINTS_CHANGE_STATUS_SUCCESS

I've tried this to print the instances capturing group:

perl -ne 'print "$1\n" if /"resolution.(\w+)/g' FILE_NAME
perl -ne 'print "$1\n" if m/"resolution.(\w+)/sig' FILE_NAME

I'm expecting to get:

OPEN_IT_COMPLAINTS_FOUND
OPEN_IT_COMPLAINTS_NOT_FOUND
MCS_GET_COMPLAINTS_WARNING
MCS_GET_COMPLAINTS_TIMEOUT
MCS_GET_COMPLAINTS_FAIL

But I'm only getting:

OPEN_IT_COMPLAINTS_FOUND

If I put the above mentioned kind of expressions each in a single line inside the file as this:

\"rrcodes\":\"resolution.OPEN_IT_COMPLAINTS_NOT_FOUND1\"
\"rrcodes\":\"resolution.OPEN_IT_COMPLAINTS_NOT_FOUND2\"
\"rrcodes\":\"resolution.OPEN_IT_COMPLAINTS_NOT_FOUND2\"
\"rrcodes\":\"resolution.OPEN_IT_COMPLAINTS_NOT_FOUND4\"

I do get the expected output:

OPEN_IT_COMPLAINTS_NOT_FOUND1
OPEN_IT_COMPLAINTS_NOT_FOUND2
OPEN_IT_COMPLAINTS_NOT_FOUND2
OPEN_IT_COMPLAINTS_NOT_FOUND4

Upvotes: 1

Views: 292

Answers (4)

Charles Jie
Charles Jie

Reputation: 373

It's very easy. Just save all the matched in an array @x:

perl -ne 'say join "\n",@x if @x=/\"resolution.(\w+)/g' FILE_NAME

@x will be evaluated (by if) to be true if it's not empty.

Upvotes: 1

Steffen Ullrich
Steffen Ullrich

Reputation: 123561

if /.../g is using the regex in a scalar context. In this context /.../g is returning only a single match. The next /.../g will return the next single match etc:

$ perl -ne '
   print "$1\n" if /"resolution.(\w+)/g; 
   print "$1\n" if /"resolution.(\w+)/g;' file
COMPLAINTS_CHANGE_STATUS_SUCCESS
MCS_CHANGE_COMPLAINTS_STATUS_TIMEOUT

If you want all matches you have to either call /.../g repeatedly in scalar context or use it in array context. The first option would look like this:

$ perl -ne 'print "$1\n" while /"resolution.(\w+)/g' file
COMPLAINTS_CHANGE_STATUS_SUCCESS
MCS_CHANGE_COMPLAINTS_STATUS_TIMEOUT
MCS_CHANGE_COMPLAINTS_STATUS_WARNING
COMPLAINTS_CHANGE_STATUS_FAIL

In array context /.../g returns all matches at once as an array, i.e.

@matches = /"resolution.(\w+)/

Within some command line statement this could look like this:

$ perl -ne 'print map { "$_\n" } /"resolution.(\w+)/g' 
COMPLAINTS_CHANGE_STATUS_SUCCESS
MCS_CHANGE_COMPLAINTS_STATUS_TIMEOUT
MCS_CHANGE_COMPLAINTS_STATUS_WARNING
COMPLAINTS_CHANGE_STATUS_FAIL

Upvotes: 2

Stefan Becker
Stefan Becker

Reputation: 5972

Scalar vs. list context:

$ perl -ne 'print "$_\n" foreach (/"resolution\.(\w+)/g)' dummy.txt
COMPLAINTS_CHANGE_STATUS_SUCCESS
MCS_CHANGE_COMPLAINTS_STATUS_TIMEOUT
MCS_CHANGE_COMPLAINTS_STATUS_WARNING
COMPLAINTS_CHANGE_STATUS_FAIL

i.e. /g only returns multiple results if the regex gets repeatedly executed. Your command line only executed the match once.

Maybe the following code example makes it more clear;

#!/usr/bin/perl
use strict;
use warnings;

my $re    = qr/string\.(\w+)/;
my $input = "asdlkj string.TEST1 daklkl string.TEST2 kasöldk";

my($scalar) = ($input =~ /$re/g);
print "SCALAR: $scalar\n";

my @array   = ($input =~ /$re/g);
print "ARRAY:  @array\n";

exit 0;

Upvotes: 1

Tanktalus
Tanktalus

Reputation: 22294

I don't see OPEN_IT in your input file.

Try looping over the matches instead:

perl -ne 'print "$_\n" for (/"resolution.(\w+)/g)' FILE_NAME

The parenthesis give the match a list context, the for will set $_ to each match one at a time and call print "$_\n" on each.

Upvotes: 1

Related Questions