How do you completely escape the REPLACEMENT part of a perl subsitution with the s//
operator? \Q
and \E
do not work as, shown below:
For context, this comes up when using perl to do big recursive search and replace operations driven from a bash script. This is not an easily avoidable situation.
Take this script for example:
$ cat example.sh
#!/bin/bash
set -v -x
[email protected]
echo "EMAIL = $EMAIL"
echo "Email address: here" | perl -p -e "s/here/$EMAIL/"
echo "Email address: here" | perl -p -e "s/here/\\Q$EMAIL\\E/"
echo "Email address: here" | perl -p -e "s/here/${EMAIL/@/\\@}/"
Let's run it:
$ ./example.sh
[email protected]
+ [email protected]
echo "EMAIL = $EMAIL"
+ echo 'EMAIL = [email protected]'
EMAIL = [email protected]
So far so good. The shell isn't mangling anything and we are echoing what we expect.
echo "Email address: here" | perl -p -e "s/here/$EMAIL/"
+ echo 'Email address: here'
+ perl -p -e s/here/[email protected]/
Email address: user.org
Okay, that time the replacement wasn't quoted, so the @example
part of the string got expanded (to nothing) and effectively disappeared. Okay, fine, let's escape it with our good friends \Q
and \E
:
echo "Email address: here" | perl -p -e "s/here/\\Q$EMAIL\\E/"
+ echo 'Email address: here'
+ perl -p -e 's/here/\[email protected]\E/'
Email address: user\.org
Well, that was unexpected! \Q
and \E
quoted the .
, but they left the @example
part unescaped! What is going on here?
echo "Email address: here" | perl -p -e "s/here/${EMAIL/@/\\@}/"
+ echo 'Email address: here'
+ perl -p -e 's/here/user\@example.org/'
Email address: [email protected]
Okay, so this finally worked, but only because we used bash pattern expansion to do a search and replace. It worked in this particular case because this was an e-mail address. This would be incredibly tedious to do for all possible replacement metacharacters in a more general case.
So again, how do you escape the REPLACEMENT part of a perl subsitution completely when using the s//
operator? Is it possible? There has to be a trick I'm missing. =)
SOLVED
ysth's answer suggested using s'''
, which solves this simple example, but I couldn't use that in my real code, because I needed backreferences in my real use caes. However, ysth's answer and TLP's comment both proposed using $ENV{...}
. As far as I can tell, this works perfectly so far in my real use cases, which must be able to use back-references.
Here is an updated version of the example shown above.
$ cat example-new.sh
#!/bin/bash
set -v -x
[email protected]
# Don't touch my delimiters!
echo "Email address goes >>>>>>here<<" | perl -p -e 's/(>+)here(<+)/$1$ENV{EMAIL}$2/'
It works as expected when running it:
$ ./example-new.sh
[email protected]
+ [email protected]
# Don't touch my delimiters!
echo "Email address goes >>>>>>here<<" | perl -p -e 's/(>+)here(<+)/$1$ENV{EMAIL}$2/'
+ echo 'Email address goes >>>>>>here<<'
+ perl -p -e 's/(>+)here(<+)/$1$ENV{EMAIL}$2/'
Email address goes >>>>>>[email protected]<<
\Q\E are applied to the results of variable interpolation, so you can't keep @example from interpolating that way.
But you can use single quotes:
#!/bin/bash
set -v -x
[email protected]
echo "Email address: here" | perl -p -e "s'here'$EMAIL'"
or, if the email address may contain '
or \\
, let perl get $EMAIL from the environment:
export [email protected]
echo "Email address: here" | perl -p -e 's/here/$ENV{EMAIL}/'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With