I'm facing strange behaviour when using '?' regexp repetition. I'm processing log file where I'm searching for specific HTTP error responses, eg. 401. The line may but may not contain body of the response. So I want to match both cases. I have following code.
#!/usr/bin/perl
$match = 'response 401';
$line = '2021-04-08 07:15:01 | INFO | [http-nio-8080-exec-11] | rId:123456789 | ip:127.0.0.1 | activationId: abcdefg | user: admin | response 401: headers: Cache-Control: [no-cache, no-store, max-age=0, must-revalidate] / Content-Length: [60] / Content-Type: [application/json;charset=UTF-8] / Date: [Thu, 08 Apr 2021 05:15:01 GMT] / Expires: [0] / Pragma: [no-cache] | body: {"errors":[{"message":"Bad credentials","repeatable":true}]}';
my($tstamp, $level, $thread, $body) = $line =~ m/^(.*?)\s+\|\s+(\w+)\s+\|\s+\[(.*?)\].*?$match.*?(?:body\:\s+({.*}))?/;
if($body) {
print "body: $body\n";
}
This won't print anything. I would expect it should as the .*?$match.*? should match the smallest part of line and leave enough for body pattern. But obviously it won't.
When I change the regexp and remove ? from body pattern and make it mandatory the line matches.
my($tstamp, $level, $thread, $body) = $line =~ m/^(.*?)\s+\|\s+(\w+)\s+\|\s+\[(.*?)\].*?$match.*?(?:body\:\s+({.*}))/;
But this won't match lines where there is no body.
What's wrong with the regexp? I suspect the non-greedy .*? pattern preceding (?:body...)? pattern eats the input as it's ok with the optional body.
How to write the correct regexp?