13

If a regular expression match occurs inside an eval, changes to the capture-related variables ($1, etc.) are not visible in the outside environment. Is this a bug?

perlop and perlre don't seem to mention any such restriction.

For example:

 use strict; use warnings;
 $_ = "hello";
 eval '/(.*)/';
 print "GOT: $1\n";

gives:

Use of uninitialized value $1 in concatenation (.) or string at -e line 1.
GOT:

A more succinct demo is:

perl -we '$_="foo"; eval q(/(.*)/;) ; print "GOT:$1\n";'
Jim Avera
  • 131
  • 3

3 Answers3

10

Documentation proof that localized variables are the issue here is in perlvar of 5.14.0:

These variables are read-only and dynamically-scoped, unless we note otherwise.

The dynamic nature of the regular expression variables means that their value is limited to the block that they are in [...]

Note that this bit of documentation is absent from the 5.12.4 perldoc.


The problem is localized variables. My copy of perldoc -f eval (5.12.4) has this to say:

The assignment to $@ occurs before restoration of localised
variables, which means a temporary is required if you want to
mask some but not all errors: [...]

The manpage doesn't make an explicit statement for all such special global variables (like $1, $&, and probably others), but block localization and subsequent restoration is what appears to happen here.

The variables are assigned to inside the eval and the original values are restored once the eval block is left.

use strict; use warnings;
use Test::More;
use constant V => 'hello';

$_ = V;

note '*** block eval';
eval {
        is $_, V, 'input ok';
        /(.*)/;
        is $&, V, 'in eval'; is $1, V, 'in eval';
};
is $&, V, 'after eval'; is $1, V, 'after eval';

note '*** without eval';
is $_, V, 'input ok';
/(.*)/;
is $&, V; is $1, V;

done_testing;
Community
  • 1
  • 1
Lumi
  • 14,775
  • 8
  • 59
  • 92
  • @Matteo - As previously stated in another comment on this page, `$1` is not "implicitly declared". See the update at the top of my answer for a doc reference explaining this issue. – Lumi Jun 06 '12 at 07:28
3

The actual question is already answered, so here's the answer how you achieve the task.

Simply use the return values of eval, which in turn uses the return values of the match operator. $1 sucks, avoid.

use strict; use warnings;
$_ = "hello";
my @r = eval '/(.*)/';
# (
#     "hello"
# )
daxim
  • 39,270
  • 4
  • 65
  • 132
2

Any lexical variables declared within an eval will be lost after the eval ends.

aleroot
  • 71,077
  • 30
  • 176
  • 213
  • 1
    I used to think of `$1` (and `$&`, for that matter) as *global* variables, not *lexical* ones. – Lumi Jun 06 '12 at 06:46
  • @Lumi they are global but are implicitly declared only by the first use (see an example in my answer) – Matteo Jun 06 '12 at 06:52
  • 1
    @Matteo - No, the notion of *declaring* global variables (really *package* variables) in Perl is misleading. There is no need to declare them, neither the built-in ones nor any a user might add. They're just values stored in a namespace. The *need for declaration or full qualification* only arises with the `strict` pragma: `my`-declaration, `our`-declaration (attached to the package but lexically scoped), or full qualification of non-built-in globals. – Lumi Jun 06 '12 at 07:25
  • @Lumi Yes I now also found the relevant documentation (that you also added to your answer). The behavior now makes sense :-) – Matteo Jun 06 '12 at 07:28