简体   繁体   中英

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=user@example.org
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=user@example.org
+ EMAIL=user@example.org
echo "EMAIL = $EMAIL"
+ echo 'EMAIL = user@example.org'
EMAIL = user@example.org

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/user@example.org/
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/\Quser@example.org\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: user@example.org

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=user@example.org
# 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=user@example.org
+ EMAIL=user@example.org
# 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 >>>>>>user@example.org<<

\\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=user@example.org
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=user@example.org
echo "Email address: here" | perl -p -e 's/here/$ENV{EMAIL}/'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM