in reply to Side effect of using undefined variable in regex

Is this behaviour caused by using an undefined variable in a regex?

Yes, more specifically a variable that evaluates to the empty string. From Regexp Quote Like Operators:

If the PATTERN evaluates to the empty string, the last successfully matched regular expression is used instead. ... If no match has previously succeeded, this will (silently) act instead as a genuine empty pattern (which will always match).

Update: Added the second half of the perlop quote, and see also my reply further down in this thread.

Replies are listed 'Best First'.
Re^2: Side effect of using undefined variable in regex
by Eily (Monsignor) on Jul 01, 2019 at 08:22 UTC

    When I realized that, I thought this explained the behaviour. But the problem is, the regex at the top of the script is not the last regex, since there has been a successful match with /$last/ previously. I have tried replacing the "useless" match with this:

    my $supported = '4.0.0,4.0.1,4.1.0,4.1.1,4.1.2'; $supported =~ /\Q4.1.2\E(?{say '>>> In the regex'})/ if @ARGV; "4.1.2" =~ //;
    And the message does appear twice at the start, but does not appear when the special behaviour of m// is triggered.

    In all cases, use warnings; would have prevented this issue though.

    Edit: ... the (?{ }) block is never reached in the call to /$last/ because the first half of the regex does not match. PEBKAC

      Ok, I've looked at it a little further now. I omitted a bit in my quote from perlop:

      If the PATTERN evaluates to the empty string, the last successfully matched regular expression is used instead. ... If no match has previously succeeded, this will (silently) act instead as a genuine empty pattern (which will always match).

      I think there is something omitted from the documentation - AnomalousMonk describes this as well:

      $ perl -le'print"x"=~//?"yes":"no"; "y"=~/y/; print"x"=~//?"yes":"no"' yes no $ perl -le'print"x"=~//?"yes":"no";{"y"=~/y/} print"x"=~//?"yes":"no"' yes yes

      So in other words, the "last successfully matched regular expression" means the "last successfully matched regular expression in this scope", which explains why the successful match when $last is "Config" doesn't affect the other matches. I stumbled on this at first; I think it might be worth a documentation patch...

      Anyway, that means in the OP's example, we can remove startStandardServices('Config') since that's working as expected, and since the other two calls of startStandardServices and startGeneralServices are seeing the same issue each, the script can be reduced to:

      use warnings; use 5.014; my @stoppedGeneralServices = ('OP Mover', 'OP Monitor'); my $supported = '4.0.0,4.0.1,4.1.0,4.1.1,4.1.2'; $supported =~ /\Q4.1.2/ if @ARGV; startGeneralServices(); sub startGeneralServices { my $last = shift; my $name; while (@stoppedGeneralServices) { $name = pop @stoppedGeneralServices; say ' Starting $name=\'', $name, '\' $last=', defined $last ? "'$last'" : 'undef', ' ($name=~/$last/i)=', ($name =~ /$last/i) ? 1 : 0; last if $name =~ /$last/i; } }

      And taking that a few steps further:

      use warnings; use 5.014; '4.1.1,4.1.2' =~ /\Q4.1.2/ if @ARGV; my $last = ''; my $name = 'OP Mover'; say '$name=\'', $name, '\' $last=', defined $last ? "'$last'" : 'undef', ', $name=~/$last/i = ', ($name =~ /$last/i) ? 1 : 0;
      $ perl 11102215.pl $name='OP Mover' $last='', $name=~/$last/i = 1 $ perl 11102215.pl all $name='OP Mover' $last='', $name=~/$last/i = 0

      Which shows that:

      • If @ARGV is false, there is no previously executed regex, so /$last/ aka // acts like the empty pattern, which always matches.
      • If @ARGV is true, then // means to use the previous successful regex, /\Q4.1.2/, which does not match the string 'OP Mover'.

        I've edited my post because putting the (?{ }) block after trying to match 4.1.2 was the obvious reason why it didn't called again. But I've tried this:

        use v5.20.0; my @stoppedStandardServices = ( 'OP OPC Client', 'OP Calculation Serve +r', 'OP Data Server', 'OP Configuration Server', 'OP Log Server', 'OP + Time Server', 'ONC RPC PortMapper'); my $supported = '4.0.0,4.0.1,4.1.0,4.1.1,4.1.2'; $supported =~ /.(?{say '>>> In the regex'})/ if @ARGV; "4.1.2" =~ //; say "Start Basic Standard Services:"; startStandardServices( 'Config'); say "Start Remaining Standard Services:"; startStandardServices(); say "Installation completed"; sub startStandardServices { my $last = shift; my $name; while (@stoppedStandardServices) { $name = pop @stoppedStandardServices; say ' Starting $name=\'', $name, '\' $last=', defined $last ? + "'$last'" : 'undef', ' ($name=~/$last/i)=', ($name =~ /$last/i) ? 1 +: 0; last if $name =~ /$last/i; } }
        And with one parameter I get the output:
        >>> In the regex >>> In the regex Start Basic Standard Services: Starting $name='ONC RPC PortMapper' $last='Config' ($name=~/$last/i) +=0 Starting $name='OP Time Server' $last='Config' ($name=~/$last/i)=0 Starting $name='OP Log Server' $last='Config' ($name=~/$last/i)=0 Starting $name='OP Configuration Server' $last='Config' ($name=~/$la +st/i)=1 Start Remaining Standard Services: 'Config' Starting $name='OP Data Server' $last=undef ($name=~/$last/i)=1 'Config' Installation completed
        Why doesn't ">>> In the regex" appear again when $last is undef, and how is the string 'Config' printed?

        ... the "last successfully matched regular expression" means the "last successfully matched regular expression in this scope" ... I think it might be worth a documentation patch...

        I've been looking for an explicit discussion of this. Best I can come up with, and it's very indirect, is in Variables related to regular expressions:

        The dynamic nature of the regular expression variables means that their value is limited to the block that they are in ...
        I'm sure I've seen something better, more to the point. Not much time to look ATM.


        Give a man a fish:  <%-{-{-{-<

Re^2: Side effect of using undefined variable in regex
by Anonymous Monk on Jul 01, 2019 at 09:05 UTC

    Thanks

    This behaviour corresponds exactly to what I experience in my further reduced script:

    use v5.30.0; 'ABC' =~ /B/ if @ARGV; my $last; say ' (\'NAME\'=~/$last/i)=', 'NAME' =~ /$last/i ? 1 : 0;

    Output when called without arguments:

    D:\>surprise.pl ('NAME'=~/$last/i)=1

    Output when called with arguments:

    D:\>surprise.pl all ('NAME'=~/$last/i)=0