perl.gg / regex

The /ee Double-Eval Modifier

2026-03-19

This page explains how the /ee modifier works in Perl substitutions and when you should actually use it. The /e flag already tells Perl to run the replacement side as code instead of a literal string:
my $text = "price: 100"; $text =~ s~(\d+)~$1 * 1.13~e; # "price: 113"
Standard stuff. The /e flag evaluates $1 * 1.13 as an expression. Useful, well-understood.

When you add a second e, Perl runs that code twice. The first pass builds a string, and the second pass executes that string as Perl code. This creates a two-stage pipeline inside a single substitution:

$text =~ s~(\d+)~'$1 * 1.13'~ee;
The modifier is a template engine in one substitution. It is also the kind of tool that deserves a healthy dose of caution.

Part 1: HOW /ee ACTUALLY WORKS

The modifier runs the replacement string through Perl twice. The first pass evaluates the replacement as code and returns a string. The second pass takes that string and evaluates it as code again:
my $x = 10; my $text = "result is VALUE"; $text =~ s~VALUE~'$x * 2'~ee; # $text is now "result is 20"
Stage 1 (first 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.

Replacement: '$x * 2' First eval: '$x * 2' --> string: $x * 2 Second eval: $x * 2 --> number: 20 Final result: "result is 20"
The two stages let you construct code dynamically in the first pass and execute it in the second. This is a two-stage pipeline inside a regex. It is powerful, and it is dangerous.

Part 2: VARIABLE INTERPOLATION IN TEMPLATES

The classic use case is template strings where variable names appear literally:
my $name = "Alice"; my $count = 3; my $template = 'Hello $name, you have $count items'; $template =~ s~(\$\w+)~$1~gee; # "Hello Alice, you have 3 items"
Stage 1 evaluates $1, which produces the matched string (e.g., $name). Stage 2 evaluates that string as Perl code, looks up the variable, and returns "Alice". This is 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

Here is a real-world pattern. A config file with variable references:
base_dir = /opt/app log_dir = $base_dir/logs data_dir = $base_dir/data cache_dir = $data_dir/cache
You can parse and resolve this with a single /e flag, and you do not need /ee here:
my %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; }
That is a single /e with a hash lookup. It is safe and predictable. For ninety percent of config file work, this is what you want.

But what if the config supports expressions?

width = 1920 height = 1080 pixels = $width * $height
You would need to resolve variables first, then evaluate the math. That requires two stages, and that is where /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.

# 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
This is the exact class of vulnerability that has ended careers. The /ee modifier is eval in disguise, and eval on untrusted input is the oldest sin in the book.

Rules for /ee:

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.
The safe version looks like this:
# SAFE: hash lookup, no code execution $template =~ s~\$\{(\w+)\}~$vars{$1} // ''~ge;
Values come from your hash, which you control. No injection is possible.

Part 5: DYNAMIC CODE GENERATION

Where /ee genuinely shines is metaprogramming where you control both the pattern and the data. Generating code from code is the right home for this tool.

Here is a dispatch table builder:

#!/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
This uses plain eval rather than /ee, but the principle is identical. You are constructing code as strings and executing them. The /ee modifier is just a way to do this inline within a substitution. It is eval with a delivery mechanism.

Part 6: PRACTICAL NUMBER FORMATTING

Here is a benign, useful example with single /e. Formatting numbers in a report string:
my $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"
Single /e with a multi-line replacement block. You do not need /ee because you are writing the code directly, not constructing it from strings. This is the typical real-world situation.

The /ee modifier is for when the code does not exist yet at compile time and you need to build it from match results first.

Part 7: UNDERSTANDING THE SECOND EVAL

The second e is equivalent to wrapping the first result in eval:
# These are equivalent: $str =~ s~PATTERN~REPLACEMENT~ee; $str =~ s~PATTERN~ eval(do { REPLACEMENT }) ~e;
All of eval's behaviors apply. A syntax error in the second stage causes eval to catch it, set $@, and return an empty string. The failure is silent.
my $text = "value is CODE"; $text =~ s~CODE~'this is not } valid { perl'~ee; # $text is now "value is " # $@ contains the syntax error
There is no warning and no die. There is just silent failure. If you are debugging /ee substitutions, check $@ after each one.

Part 8: THE TRIPLE /eee (DON'T)

Yes, you can stack them. /eee evaluates three times. The first eval produces a string, the second eval executes it to produce another string, and the third eval executes that final string.
my $x = 42; my $text = "answer"; $text =~ s~answer~'"\\$x"'~eee;
In practice, nobody uses /eee. If you need three levels of evaluation, you do not have a regex problem. You have an architecture problem, or possibly a drinking problem. The practical ceiling is /ee, and even that is rare.

Part 9: SAFER ALTERNATIVES

Before reaching for /ee, try these approaches.

Hash lookup with /e handles ninety percent of template cases:

$text =~ s~\$\{(\w+)\}~$vars{$1} // ''~ge;
Subroutine call with /e handles complex logic:
sub resolve { my ($key) = @_; return $config{$key} if exists $config{$key}; return "[unknown: $key]"; } $text =~ s~\$(\w+)~resolve($1)~ge;
Template modules handle real templating:
use Template::Tiny; my $tt = Template::Tiny->new; $tt->process(\$template, \%vars, \$output);
Each of these is safer, more readable, and more maintainable. Use them. Save /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 does not 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.

.--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/ /e = replacement is code /ee = replacement is code that produces code /eee = you've gone too far
perl.gg