Substitution Only in Capture Groups in Perl
The title may not explain it well, so here’s an example. A regular perl substitution probably looks like this:
$ echo "hello world" | perl -pe "s/h/b/g"
bello world
The h
part is the target we want to substitute, and
b
is the substitution. If we add stuff to the original
string, and run the same substitution:
$ echo 'hello `hello world` world' | perl -pe "s/h/b/g"
bello `bello world` world
The hello
outside and inside the backticks are all
affected. If I only want to run the substitution inside the
backtick:
$ echo 'hello `hello world` world' | perl -pe 's{(`.*?`)}{$1 =~ s/h/b/gr}ge'
hello `bello world` world
Let’s break down what we did here. First of all, we changed the
convention of separating targets and subtitutions from
s/a/b/g
to s{a}{b}g
. These two are just
different ways to express the same command. Also, we changed from
double-quotes (""
) to single-quotes (''
) to
avoid our shell evaluating $1
, etc., and leave them to
perl.
On the target side, we targeted all text inside backticks
({(`.*?`)}
) and put them inside a capture group. On the
substitution side, we did a trick — write the substitution as code. The
{$1 =~ s/h/b/gr}
grabs the first capture group (all text
inside backticks in our case), substituted h
with
b
, and set the global flag (g
) and the non
destructive substitution flag (r
). What the r
flag does is it specifies the result only be shown in the output, but
not written back to the variable ($1
in our case).
The last part, ge
, specifies that the substitution is
global (g
), and should be evaluated before output
(e
).
To make the substitution work multiline, add 0
to the
argument list, and add s
to the substitution flags:
$ printf 'hello `hello\nworld` world\n' | perl -0pe 's{(`.*?`)}{$1 =~ s/h/b/gr}ges'
hello `bello
world` world
Last updated: 2022-10-06