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