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

Hi, In the below code, If the 'line' matches with any of 'var1' or 'var2', then only it should go into if block.

But, even though none of the variables are matching with line, the code is executing lines inside if statement.

Can you please explain what is happening here ?

$line = 'xyz'; $var1 = ''; $var2 = 'abc'; $check = 'Yes'; if((($line =~ m/$var1/i) || ($line =~ m/$var2/i)) and ($check =~ m/YES +/i)) { print "Inside if\n";} else {print "Outside if\n";}

Replies are listed 'Best First'.
Re: Issue with matching regex
by Athanasius (Archbishop) on Oct 20, 2022 at 06:06 UTC

    Hello KishKishore,

    The variable $var1 contains the empty string, which matches any string (since any string, even the empty string itself, “contains” the empty string). That’s why the if block is executed.

    15:58 >perl -wE "my $line = 'xyz'; my $var1 = ''; say 'Match' if $line + =~ /$var1/i;" Match 15:59 >

    Hope that helps,

    Athanasius <°(((><contra mundum סתם עוד האקר של פרל,

      It's even worse than that. When $var is emmpty, /$var/ matches anything only if there was no successful match before.

      E.g. if you prepend this at the beginning of the script:

      "abc" =~ /a/;

      it suddenly starts outputting "Outside if", because /$var/ runs the last successfully matched regex if there was any (see The empty pattern //). If you don't want this behaviour (and I bet you don't), never use a match with fully dynamic contents. You can change it to /(?:$var)/, so that the pattern doesn't get empty after interpolating the variable.

      Update: There probably should be a perlcritic policy forbidding a potentially empty regex match.

      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

        The even better news is that the original if condition can be much simplified and by combining the first 2 regexen it can avoid the fully empty pattern into the bargain:

        if ($line =~ /$var1|$var2/i && $check =~ /YES/i) { print "Inside if\n" +;} else {print "Outside if\n";}

        Of course if we still have $var1 = '' then it will still match but that's a question for KishKishore as to what they actually intend.


        🦛

        Good point!
        So, e.g. by reversing the order of substatements inside the IF statement gives opposite result.
        if(($check =~ m/YES/i) and (($line =~ m/$var1/i) || ($line =~ m/$var2/ +i))) { print "Inside if\n";} else {print "Outside if\n";}
        ...unless ($check =~ m/Y/i) :)

        Thanks choroba,

        I didn’t know that! This special case must have been implemented for a reason, but I can’t think what it could have been.

        Using a non-capturing group to avoid this behaviour is a useful workaround.

        The good news is that this special case doesn’t seem to be present in Raku:

        23:48 >perl -wE "my $s = ''; say 'match1' if 'abc' =~ /b/; say 'match2 +' if 'adc =~ /(?:$s)/;" match1 match2 23:55 >perl -wE "my $s = ''; say 'match1' if 'abc' =~ /b/; say 'match2 +' if 'adc =~ /$s/;" match1 23:56 >raku -e "my Str $s = ''; say 'match1' if 'abc' ~~ /b/; say 'mat +ch2' if 'adc' ~~ /$s/;" match1 match2 23:56 >

        Cheers,

        Athanasius <°(((><contra mundum סתם עוד האקר של פרל,

Re: Issue with matching regex
by jwkrahn (Abbot) on Oct 20, 2022 at 06:12 UTC
    $ perl -e' $line = "xyz"; $var1 = ""; $var2 = "abc"; #check = "Yes"; if ( ( ( $line =~ m/$var1/i ) || ( $line =~ m/$var2/i ) ) and ( $check + =~ m/YES/i ) ) { print "Inside if\n"; } else { print "Outside if\n"; } ' Outside if

    Is this what you are seeing? What is the problem?

    Sorry, typo on line 5.