如何确保插入到“ sed”替换中的字符串转义所有元字符


21

我有一个脚本,该脚本读取文本流并生成sed命令文件,该文件随后与一起运行sed -f。生成的sed命令类似于:

s/cid:image002\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1922/g
s/cid:image003\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1923/g
s/cid:image004\.jpg@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1924/g

假设生成sed命令的脚本类似于:

while read cid fileid
do
    cidpat="$(echo $cid | sed -e s/\\./\\\\./g)"
    echo 's/'"$cidpat"'/https:\/\/mysite.com\/files\/'"$fileid"'/g' >> sedscr
done

如何改善脚本以确保cid字符串中的所有正则表达式元字符均已转义并正确插值?

Answers:


24

为了逃避变量上的左侧和右侧可以使用s命令sed(这里$lhs$rhs分别),你会怎么做:

escaped_lhs=$(printf '%s\n' "$lhs" | sed 's:[][\/.^$*]:\\&:g')
escaped_rhs=$(printf '%s\n' "$rhs" | sed 's:[\/&]:\\&:g;$!s/$/\\/')

sed "s/$escaped_lhs/$escaped_rhs/"

请注意,$lhs不能包含换行符。

也就是说,在LHS上,转义所有正则表达式运算符(][.^$*),转义字符本身(\)和分隔符(/)。

在RHS上,您只需要转义&,分隔符,反斜杠和换行符(您可以通过在每行的末尾插入反斜杠(最后一行($!s/$/\\/)除外)来完成此操作)。

这假设你用/在你的分隔符sed s的命令和你没有启用扩展的RE-r(GNU sed/ ssed/ ast/ busybox sed)或-E(BSD系统,ast最近GNU,最近busybox的)或PCREs-Rssed)或增强的RE-A/ -Xast),它都有额外的RE运算符。

处理任意数据时的一些基本规则:

  • 不要使用 echo
  • 引用您的变量
  • 考虑语言环境的影响(尤其是其字符集:例如,使用转义的字符串(并使用相同的命令),转义 sed命令在与sed命令相同的语言环境中运行很重要)sed
  • 不要忘了换行符(在这里您可能要检查是否$lhs包含换行符并采取措施)。

另一种选择是在环境中使用perl代替sed并传递字符串,并使用\Q/ \E perlregexp运算符从字面上获取字符串:

A="$lhs" B="$rhs" perl -pe 's/\Q$ENV{A}\E/$ENV{B}/g'

perl(默认情况下)不会受到语言环境的字符集的影响,因为在上文中,它仅将字符串视为字节数组,而不关心它们可以为用户代表什么字符(如果有)。使用sed,您可以通过将所有命令的语言环境固定为Cwith LC_ALL=C来实现相同的目的sed(尽管这也会影响错误消息的语言,如果有的话)。


如果我需要转义双引号怎么办?
梅农2015年

@Menon,双引号不是特殊的sed,您不需要转义它们。
斯特凡Chazelas

这不能用于使用通配符的模式匹配吗?
梅农2015年

@Menon,不,通配符模式匹配与find-name是从正则表达式不同。在那里,你只需要逃跑?*反斜线和[
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.