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

I am attempting to use a Path::Iterator::Rule "Custom rule subroutine".

I have a regexp during the buildup of my rules, which determines whether the custom rule subroutine is used. If it indicates to use the subroutine, then its capture group $1 at this point holds some text that I want to match in the subroutine to filter for certain pathnames.

With the naive approach, it seems that the $1 or $var variable in my regexp is only expanded at the time of the regexp/subroutine being run. At this point, the variable is no longer valid and could have been overwritten by some other value. Naive code:

my $rule = Path::Iterator::Rule->new; if ($id =~ /^([0-9A-F]{2})/) { my $int = $1; $rule->and( sub { m#/INT $int# } ); }

Resulting errors are many times this line:

Use of uninitialized value $int in regexp compilation at [redacted]/proj/intlist/intlist.pl line 1167.

I found that it appears to work if I use a "postponed" regular subexpression like so:

my $rule = Path::Iterator::Rule->new; if ($id =~ /^([0-9A-F]{2})/) { my $int = $1; $rule->and( sub { m#/INT (??{ "$int" })# } ); }

Is this a correct approach? Are there other ways to interpolate a variable at the time of adding the custom rule subroutine, so that the rule doesn't refer to the variable later but rather uses the text that it used to hold?

  • Comment on Interpolate variable into regexp at time of definition rather than execution, as a filter for Path::Iterator::Rule
  • Select or Download Code

Replies are listed 'Best First'.
Re: Interpolate variable into regexp at time of definition rather than execution, as a filter for Path::Iterator::Rule
by ikegami (Patriarch) on Apr 27, 2026 at 21:22 UTC

    At this point, the variable is no longer valid and could have been overwritten by some other value.

    This isn't true.

    Resulting errors are many times this line:

    And neither is this. You can't get that error from the code you posted.


    The code is actually correct. Yes, $int goes out of scope on the last line of the snippet. But that only limits its visibility, not its lifespan. By capturing the lexical, the anon sub extends its life.

    Even if the code is called in a loop, everything will work. A new $int will be created. This is easy to demonstrate:

    my @subs; for (qw( 123 456 789 )) { my $int = $_; push @subs, sub { $int }; } say $_->() for @subs;
    123 456 789

    In contrast, note how the following code only has one $int that's captured three times:

    my $int; my @subs; for (qw( 123 456 789 )) { $int = $_; push @subs, sub { $int }; } say $_->() for @subs;
    789 789 789

    Maybe you didn't use a properly-scoped lexical variable?

    I could have used for my $int in the first snippet, but I wanted to make the two snippets easier to contrast.

Re: Interpolate variable into regexp at time of definition rather than execution, as a filter for Path::Iterator::Rule (updated)
by LanX (Saint) on Apr 27, 2026 at 21:54 UTC
    I agree with ikegami, the closure variable $int shouldn't change.

    Unless this module is messing with internals, the most likely reason is your code snippet is not what you really run

    You said

    > Naive code:

    Could it be you forgot my or maybe $1 is undefined because your if clause does more complicated checks?

    Most importantly:

    Can you reproduce this problem with exactly the demonstrated code?

    Update

    Provided this is not an XY Problem.

    Something roughly like (untested)...

    my $code_ref = eval "sub{ m#INT $int# }" ; $rule->and($code_ref);

    ...will hardcode $int into the sub right away.

    But be careful to keep full control of $int and to catch potential eval errors in $@

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery