<!-- category: regex -->
The /ee Double-Eval Modifier
You know/e. It treats the replacement side of s~~~ as code
instead of a string:
Standard stuff. Themy $text = "price: 100"; $text =~ s~(\d+)~$1 * 1.13~e; # "price: 113"
/e flag evaluates $1 * 1.13 as an expression.
Useful, well-understood.
Now double it.
First eval produces the string$text =~ s~(\d+)~'$1 * 1.13'~ee;
"100 * 1.13". Second eval executes
that string as Perl code. Result: 113. A two-stage pipeline inside
a regex.
This is /ee. A template engine in one substitution. Let's explore
what it does, and why you should be a little scared of it.
Part 1: HOW /ee ACTUALLY WORKS
Be precise about the two stages:Stage 1 (firstmy $x = 10; my $text = "result is VALUE"; $text =~ s~VALUE~'$x * 2'~ee; # $text is now "result is 20"
e): The replacement '$x * 2' is evaluated as Perl
code. Single-quoted string, so no interpolation. Produces the literal
string $x * 2.
Stage 2 (second e): That string $x * 2 is evaluated as Perl
code. $x is 10. 10 * 2 is 20.
The two stages let you construct code dynamically in the first pass and execute it in the second. Powerful. Dangerous.Replacement: '$x * 2' First eval: '$x * 2' --> string: $x * 2 Second eval: $x * 2 --> number: 20 Final result: "result is 20"
Part 2: VARIABLE INTERPOLATION IN TEMPLATES
The classic use case. Template strings where variable names appear literally:Stage 1 evaluatesmy $name = "Alice"; my $count = 3; my $template = 'Hello $name, you have $count items'; $template =~ s~(\$\w+)~$1~gee; # "Hello Alice, you have 3 items"
$1, which produces the matched string (e.g.,
$name). Stage 2 evaluates that string as Perl code, looks up the
variable, returns "Alice".
A rudimentary template engine in one line. The regex finds things that look like variables, extracts their names, and resolves them against the current scope.
Part 3: CONFIG FILE VARIABLE EXPANSION
Real-world pattern. A config file with variable references:Parse and resolve with singlebase_dir = /opt/app log_dir = $base_dir/logs data_dir = $base_dir/data cache_dir = $data_dir/cache
/e (no /ee needed here):
That's singlemy %config; while (my $line = <$fh>) { chomp $line; next if $line =~ m~^\s*#~ || $line =~ m~^\s*$~; my ($key, $val) = split m~\s*=\s*~, $line, 2; # Resolve variable references $val =~ s~\$(\w+)~$config{$1} // "\$$1"~ge; $config{$key} = $val; }
/e with a hash lookup. Safe, predictable. For 90%
of config file work, this is what you want.
But what if the config supports expressions?
Now you'd need to resolve variables first, then evaluate the math. Two stages. That's wherewidth = 1920 height = 1080 pixels = $width * $height
/ee starts whispering in your ear.
Part 4: THE SECURITY PROBLEM
Giant letters time. /ee evaluates arbitrary strings as Perl code.If any part of the matched text comes from user input, you have a code injection vulnerability. Not "maybe." Not "in theory." Right now. Game over.
This is the exact class of vulnerability that has ended careers. The# DANGEROUS: user input gets executed as code my $user_input = 'system("rm -rf /")'; my $template = 'value is $user_input'; $template =~ s~(\$\w+)~$1~gee; # You just executed arbitrary system commands
/ee modifier is eval in disguise, and eval on untrusted input
is the oldest sin in the book.
Rules for /ee:
The safe version:1. Never use /ee on strings containing user input. Ever. 2. If you think the input is sanitized, reread rule 1. 3. Use a hash lookup with single /e instead. 4. If you need expression evaluation, use a safe evaluator module.
Values come from your hash, which you control. No injection possible.# SAFE: hash lookup, no code execution $template =~ s~\$\{(\w+)\}~$vars{$1} // ''~ge;
Part 5: DYNAMIC CODE GENERATION
Where/ee genuinely shines is metaprogramming where you control
both the pattern and the data. Generating code from code.
Here's a dispatch table builder:
This uses plain#!/usr/bin/env perl use strict; use warnings; use feature 'say'; my %op_sym = (add => '+', subtract => '-', multiply => '*'); my %dispatch; for my $op (keys %op_sym) { my $sym = $op_sym{$op}; $dispatch{$op} = eval "sub { \$_[0] $sym \$_[1] }"; } say $dispatch{add}->(3, 4); # 7 say $dispatch{multiply}->(5, 6); # 30
eval rather than /ee, but the principle is
identical: constructing code as strings and executing them. The /ee
modifier is just a way to do this inline within a substitution. It's
eval with a delivery mechanism.
Part 6: PRACTICAL NUMBER FORMATTING
Here's a benign, useful example with single/e. Formatting numbers
in a report string:
Singlemy $report = "Revenue: 1234567.89 dollars, 987654 units sold"; $report =~ s~(\d+)(?=\.\d+|\b)~ my $n = $1; 1 while $n =~ s~(\d)(\d{3}(?:,\d{3})*(?:\.\d+)?$)~$1,$2~; $n; ~ge; # "Revenue: 1,234,567.89 dollars, 987,654 units sold"
/e with a multi-line replacement block. You don't need /ee
because you're writing the code directly, not constructing it from
strings. This is the typical real-world situation.
The /ee modifier is for when the code doesn't exist yet at compile
time and you need to build it from match results first.
Part 7: UNDERSTANDING THE SECOND EVAL
The seconde is equivalent to wrapping the first result in eval:
All of# These are equivalent: $str =~ s~PATTERN~REPLACEMENT~ee; $str =~ s~PATTERN~ eval(do { REPLACEMENT }) ~e;
eval's behaviors apply. Syntax error in the second stage?
eval catches it, sets $@, and the substitution produces an empty
string. Silently.
No warning. No die. Just silent failure. If you're debuggingmy $text = "value is CODE"; $text =~ s~CODE~'this is not } valid { perl'~ee; # $text is now "value is " # $@ contains the syntax error
/ee
substitutions, check $@ after each one.
Part 8: THE TRIPLE /eee (DON'T)
Yes, you can stack them./eee evaluates three times. First eval
produces a string, second eval executes it to produce another string,
third eval executes that.
In practice, nobody usesmy $x = 42; my $text = "answer"; $text =~ s~answer~'"\\$x"'~eee;
/eee. If you need three levels of
evaluation, you don't have a regex problem. You have an architecture
problem. Or a drinking problem. Possibly both.
The practical ceiling is /ee, and even that is rare.
Part 9: SAFER ALTERNATIVES
Before reaching for/ee, try these:
Hash lookup with /e (90% of template cases):
Subroutine call with /e (complex logic):$text =~ s~\$\{(\w+)\}~$vars{$1} // ''~ge;
Template modules (real templating):sub resolve { my ($key) = @_; return $config{$key} if exists $config{$key}; return "[unknown: $key]"; } $text =~ s~\$(\w+)~resolve($1)~ge;
Each of these is safer, more readable, and more maintainable. Use them. Saveuse Template::Tiny; my $tt = Template::Tiny->new; $tt->process(\$template, \%vars, \$output);
/ee for the genuinely rare case where you need to
evaluate dynamically constructed Perl code inline, and you have total
control over the input.
Part 10: RESPECTING THE POWER
The/ee modifier exists because Perl respects you enough to hand
you live ammunition. It doesn't hide dangerous features behind
warnings and advisory committees. It gives you the tool and trusts
you to point it in the right direction.
That trust is the essence of Perl. The language gives you eval,
/ee, symbolic references, and a dozen other ways to shoot yourself
in the foot. It also gives you strict, warnings, taint mode, and
decades of production wisdom saying when to use each one.
Know it exists. Understand how it works. Use it when nothing else fits. And never, ever point it at user input.
perl.gg.--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/ /e = replacement is code /ee = replacement is code that produces code /eee = you've gone too far