Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to escape the REPLACEMENT in a perl substitution?

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]<<
like image 941
wjl Avatar asked Nov 25 '13 00:11

wjl


1 Answers

\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}/'
like image 80
ysth Avatar answered Nov 15 '22 05:11

ysth