Perl substitution
last modified October 20, 2026
The s/// operator searches a string for a regular expression and
replaces the matches. It is Perl's main tool for editing text: it can clean
input, reformat data, expand templates and compute new values from the matched
text.
Basic substitution
The operator s/PATTERN/REPLACEMENT/FLAGS searches a string for a
pattern and replaces the match. It works on $_ or on the variable
bound with =~ and changes the string in place. By default only the
first match is replaced.
#!/usr/bin/perl use strict; use warnings; use v5.34.0; my $text = 'I like cats. Cats are great, cats are fun.'; (my $first = $text) =~ s/cats/dogs/; say "first only: $first"; (my $all = $text) =~ s/cats/dogs/g; say "all lower: $all"; (my $nocase = $text) =~ s/cats/dogs/gi; say "ignore case: $nocase"; my $copy = $text; my $n = ($copy =~ s/cats/dogs/gi); say "replacements made: $n"; my $none = 'nothing here'; my $zero = ($none =~ s/cats/dogs/); say "no match returns: '", $zero, "'";
The first substitution replaces only the first lowercase cats, and
with /g every occurrence is replaced. Adding /i makes
the match case-insensitive, so Cats is replaced as well. The
operator returns the number of replacements made, which is useful for counting.
When nothing matches, it returns an empty string (false) and leaves the string
unchanged.
$ perl basic.pl first only: I like dogs. Cats are great, cats are fun. all lower: I like dogs. Cats are great, dogs are fun. ignore case: I like dogs. dogs are great, dogs are fun. replacements made: 3 no match returns: ''
Captures in the replacement
Parentheses in the pattern capture text that can be reused in the replacement
through $1, $2 and so on. This allows reordering and
wrapping parts of a string. Inside the pattern, \1 refers to the
first capture.
#!/usr/bin/perl use strict; use warnings; use v5.34.0; my $name = 'Smith, John'; $name =~ s/^(\w+),\s*(\w+)$/$2 $1/; say $name; my $date = '2026-10-20'; $date =~ s/(\d+)-(\d+)-(\d+)/$3.$2.$1/; say $date; my $doubled = 'this is is a test test'; $doubled =~ s/\b(\w+)\s+\1\b/$1/g; say $doubled; my $wrap = 'important'; $wrap =~ s/^(.*)$/<b>$1<\/b>/; say $wrap;
The first two substitutions reorder a name and a date. The third one uses the
back-reference \1 inside the pattern to find a repeated word and
replaces both copies with a single one. The last example wraps the whole string
in tags. Note that the closing slash in </b> must be escaped,
or a different delimiter used.
$ perl captures.pl John Smith 20.10.2026 this is a test <b>important</b>
The /e flag
With /e the replacement part is evaluated as Perl code and its
result is inserted. The flag turns the replacement into an expression, so
arithmetic and function calls are possible. A doubled /ee evaluates
the result a second time.
#!/usr/bin/perl use strict; use warnings; use v5.34.0; my $prices = 'apple 3, pear 5, plum 12'; (my $doubled = $prices) =~ s/(\d+)/$1 * 2/ge; say "doubled: $doubled"; my $text = 'hello big world'; (my $title = $text) =~ s/(\w+)/\u$1/g; say "title case: $title"; (my $caps = $text) =~ s/(\w+)/ucfirst($1)/ge; say "with function: $caps"; my $expr = 'compute: 2+3 and 4*5'; $expr =~ s/(\d+)([+*])(\d+)/"$1$2$3=" . eval "$1$2$3"/ge; say $expr; my $tpl = 'result is $value'; my $value = 42; $tpl =~ s/(\$\w+)/$1/ee; say $tpl;
The first substitution doubles every number. The \u escape
upper-cases the next character in an ordinary replacement, while
ucfirst does the same job with /e. In the calculation
example the replacement builds a string and calls eval on the
operands. With /ee the text $value is first produced
as a string and then evaluated as a variable name. Use /ee only
with trusted data, because it executes strings as code.
$ perl expr.pl doubled: apple 6, pear 10, plum 24 title case: Hello Big World with function: Hello Big World compute: 2+3=5 and 4*5=20 result is 42
The /r flag
The /r flag returns the modified copy and leaves the original
alone. It makes substitution usable in expressions, in map and in
chains.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
my $original = ' padded text ';
my $trimmed = $original =~ s/^\s+|\s+$//gr;
say "[$trimmed]";
say "[$original]";
my @files = qw(report.txt data.txt notes.txt);
my @renamed = map { s/\.txt$/.bak/r } @files;
say "@renamed";
say 'chained: ', 'a.b.c' =~ s/\./-/gr =~ s/-/+/r;
my @sorted = sort map { s/\W//gr } ('c-3', 'a-1', 'b-2');
say "@sorted";
The trimmed version is produced without touching the original, which is still
padded. In map the flag avoids the usual trick of copying each
element first. Because the result is a string, the operators can be chained, as
in the a.b.c example, where the first call replaces the dots and
the second one converts only the first hyphen.
$ perl rflag.pl [padded text] [ padded text ] report.bak data.bak notes.bak chained: a+b-c a1 b2 c3
Delimiters
Any non-whitespace character can serve as the delimiter. Choosing a different
one avoids escaping slashes in paths and URLs. Bracketing delimiters such as
{} allow whitespace and comments between the two parts when
combined with /x.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
my $path = '/usr/local/bin/perl';
(my $p1 = $path) =~ s/\/usr\/local/\/opt/;
say "escaped slashes: $p1";
(my $p2 = $path) =~ s{/usr/local}{/opt};
say "braces: $p2";
(my $p3 = $path) =~ s|/usr/local|/opt|;
say "pipes: $p3";
(my $p4 = $path) =~ s{
/usr # prefix
/local # subdirectory
}{/opt}x;
say "with /x: $p4";
my $str = 'cost: $5';
(my $p5 = $str) =~ s'\$5'$10';
say "single quote delimiter: $p5";
All four substitutions produce the same result, but the versions with braces or
pipes need no backslashes. With /x the pattern part can be spread
over several lines with comments. Using single quotes as the delimiter turns off
interpolation in both parts, so $5 and $10 are literal
text.
$ perl delims.pl escaped slashes: /opt/bin/perl braces: /opt/bin/perl pipes: /opt/bin/perl with /x: /opt/bin/perl single quote delimiter: cost: $10
Lookups and templates
Together with /e, a hash can drive the replacement. This is a
compact way to expand abbreviations or fill in templates, and the defined-or
operator // supplies a fallback for missing keys.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
my %abbrev = (
btw => 'by the way',
imo => 'in my opinion',
fyi => 'for your information',
);
my $msg = 'btw, imo this is fine, fyi.';
(my $long = $msg) =~ s/\b(\w+)\b/$abbrev{lc $1} \/\/ $1/ge;
say $long;
my %vars = (name => 'Alice', day => 'Monday');
my $tpl = 'Hello {{name}}, see you on {{day}}. {{missing}}';
$tpl =~ s/\{\{(\w+)\}\}/$vars{$1} \/\/ "[$1?]"/ge;
say $tpl;
my $n = '1234567.891';
1 while $n =~ s/^(\d+)(\d{3})/$1,$2/;
say $n;
The first substitution replaces each word by its expansion if the hash has one,
and keeps the word otherwise. The template example fills the
{{name}} placeholders and marks the unknown one. The last loop
inserts thousands separators by repeatedly applying the substitution until it no
longer matches, using the statement modifier form 1 while ....
$ perl lookup.pl by the way, in my opinion this is fine, for your information. Hello Alice, see you on Monday. [missing?] 1,234,567.891
Substitution on lists and lines
A substitution applied to $_ works with for, so a
whole array can be edited in place. The /m flag makes
^ match at the start of every line.
#!/usr/bin/perl
use strict;
use warnings;
use v5.34.0;
my @lines = (' one ', ' two', 'three ');
s/^\s+|\s+$//g for @lines;
say join '|', @lines;
my @words = qw(apple banana cherry);
s/a/A/ for @words;
say "@words";
my $text = "line1\nline2\nline3";
(my $indented = $text) =~ s/^/ /mg;
say $indented;
my $count = () = $text =~ /line/g;
say "lines: $count";
The first two loops modify the array elements directly because $_
is an alias. The s/^/ /mg call indents every line of a
multi-line string. The final line counts matches with the () = ...
idiom, since a match in list context returns all matches.
$ perl list.pl
one|two|three
Apple bAnana cherry
line1
line2
line3
lines: 3
Best Practices
- Choose a delimiter that avoids escaping:
s{...}{...}ors|...|...|are clearer for paths. - Use /r to keep the original: especially in
mapand chained calls. - Prefer /e over /ee: the second evaluation runs strings as code and is risky with user data.
- Check the return value: the operator returns the number of replacements, or false when nothing matched.
- Anchor trimming patterns:
s/^\s+|\s+$//gremoves whitespace only at the ends.
In this article we have covered the s/// operator: basic
replacement, captures, the /g, /i, /e,
/r and /m flags, alternative delimiters, and lookups.
Source
perlop: Regexp Quote-Like Operators
Author
List all Perl tutorials.