如何在 perl 替换中转义 REPLACEMENT?

wjl*_*wjl 5 regex bash perl replace escaping

你如何完全逃脱 perl 替换的 REPLACEMENT 部分s// \Q并且\E不会因为工作,如下图所示:

对于上下文,当使用 perl 执行由 bash 脚本驱动的大型递归搜索和替换操作时会出现这种情况。这不是一个容易避免的情况。

以这个脚本为例:

$ 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/@/\\@}/"
Run Code Online (Sandbox Code Playgroud)

让我们运行它:

$ ./example.sh
EMAIL=user@example.org
+ EMAIL=user@example.org
echo "EMAIL = $EMAIL"
+ echo 'EMAIL = user@example.org'
EMAIL = user@example.org
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好。外壳没有破坏任何东西,我们正在回应我们的期望。

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
Run Code Online (Sandbox Code Playgroud)

好的,那一次替换没有被引用,所以@example字符串的一部分被扩展(到没有)并有效地消失了。好吧,好吧,让我们\Q和我们的好朋友一起逃脱吧\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
Run Code Online (Sandbox Code Playgroud)

嗯,出乎意料!\Q\E引用了.,但他们留下了未@example转义的部分!这里发生了什么?

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
Run Code Online (Sandbox Code Playgroud)

好的,这终于奏效了,但这只是因为我们使用了 bash 模式扩展来进行搜索和替换。它在这种特殊情况下有效,因为这是一个电子邮件地址。在更一般的情况下,对于所有可能的替换元字符,这样做会非常乏味。

那么,在使用s//运算符时,如何完全避免 perl 替换的 REPLACEMENT 部分?是否可以?一定有我错过的技巧。=)

解决了

ysth 的回答建议使用s''',它解决了这个简单的例子,但我不能在我的实际代码中使用它,因为我在我的实际使用中需要反向引用。但是,ysth 的回答和 TLP 的评论都建议使用$ENV{...}. 据我所知,到目前为止,这在我的实际用例中非常有效,它必须能够使用反向引用。

这是上面显示的示例的更新版本。

$ 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/'
Run Code Online (Sandbox Code Playgroud)

它在运行时按预期工作:

$ ./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<<
Run Code Online (Sandbox Code Playgroud)

yst*_*sth 6

\Q\E 应用于变量插值的结果,因此您不能阻止@example 以这种方式进行插值。

但是您可以使用单引号:

#!/bin/bash
set -v -x
EMAIL=user@example.org
echo "Email address: here" | perl -p -e "s'here'$EMAIL'"
Run Code Online (Sandbox Code Playgroud)

或者,如果电子邮件地址可能包含'\\,让 perl 从环境中获取 $EMAIL:

export EMAIL=user@example.org
echo "Email address: here" | perl -p -e 's/here/$ENV{EMAIL}/'
Run Code Online (Sandbox Code Playgroud)