转义sed替换模式的字符串


317

在我的bash脚本中,我有一个外部(从用户那里收到的)字符串,我应该在sed模式中使用它。

REPLACE="<funny characters here>"
sed "s/KEYWORD/$REPLACE/g"

我如何转义$REPLACE字符串,以便将其安全地接受sed为文字替换?

注:KEYWORD是一个愚蠢的字符串没有匹配等,这是不是由用户提供。


13
如果他们说“ / g -e's / PASSWORD =。* / PASSWORD = abc / g'”,是否要避免“小鲍比表”问题?
Paul Tomblin,2009年

2
如果使用bash,则不需要sed。只需使用outputvar="${inputvar//"$txt2replace"/"$txt2replacewith"}".
destenson 2015年

@destenson我认为您不应该将两个变量放在引号之外。Bash可以读取双引号内的变量(在您的示例中,空格可能会使情况更糟)。
卡米洛·马丁


1
@CamiloMartin,请参阅我对自己答案的评论。$ {}中的引号与其中的引号不匹配。这两个变量不在引号内。
destenson

Answers:


268

警告:这并没有考虑换行。有关更深入的答案,请参阅此SO问题。(感谢Ed Morton和Niklas Peter)

请注意,转义所有内容不是一个好主意。Sed需要转义许多字符才能获得其特殊含义。例如,如果您在替换字符串中转义了一个数字,它将变成反向引用。

正如本·布兰克(Ben Blank)所说,替换字符串中只需要转义三个字符(转义自身,语句末尾用正斜杠和&替换所有字符):

ESCAPED_REPLACE=$(echo $REPLACE | sed -e 's/[\/&]/\\&/g')
# Now you can use ESCAPED_REPLACE in the original sed statement
sed "s/KEYWORD/$ESCAPED_REPLACE/g"

如果您需要转义KEYWORD字符串,则需要以下内容:

sed -e 's/[]\/$*.^[]/\\&/g'

可以由以下人员使用:

KEYWORD="The Keyword You Need";
ESCAPED_KEYWORD=$(echo $KEYWORD | sed -e 's/[]\/$*.^[]/\\&/g');

# Now you can use it inside the original sed statement to replace text
sed "s/$ESCAPED_KEYWORD/$ESCAPED_REPLACE/g"

请记住,如果您使用的字符不是/分隔符,则需要在上面的表达式中替换斜杠以及正在使用的字符。请参阅PeterJCLaw的注释以获取解释。

编辑:由于以前没有考虑某些极端情况,因此上面的命令已更改了几次。查看编辑历史记录以了解详细信息。


17
值得注意的是,通过不将正斜杠用作分隔符,可以避免不得不转义正斜杠。sed的大多数(全部?)版本都允许您使用任何字符,只要符合模式即可:$ echo'foo / bar'| sed s _ / _:_#foo:bar
PeterJCLaw 2011年

2
sed -e's /(\ / \ | \\\ | &&)/ \\&// g'在OSX上对我不起作用,但这样做:sed's /([\\\ /&])/ \\&/ g',它略短一些。
jcoffland

1
对于搜索模式KEYWORD,在GNU sed的,这里有2个字符^$上面没有提到的:s/[]\/$*.^|[]/\\&/g
Peter.O

1
@Jesse:固定。实际上,这就是我在第一段中警告的错误。我想我不练习我的讲道。
皮亚诺

1
@NeronLeVelu:我不确定我是什么意思,但是“在管道或变量中没有特殊含义。它在运行结果之前由外壳程序进行解析,因此变量内的双引号是安全的。例如,尝试A='foo"bar' echo $A | sed s/$A/baz/在bash。双引号就像在它周围的'foo'和'bar'一样对待
Pianosaurus

92

sed命令允许您使用其他字符代替/分隔符:

sed 's#"http://www\.fubar\.com"#URL_FUBAR#g'

双引号不是问题。


5
您仍然需要转义.,否则该转义具有特殊含义。我编辑了您的答案。
ypid

我刚刚尝试做:sed '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' filewith sed '|CLIENTSCRIPT="foo"|a CLIENTSCRIPT2="hello"' file和那不一样。
Dimitri Kopriwa

1
因为这仅适用于替换,所以应该说:ssed 的命令(如替换)允许您使用其他字符代替/作为分隔符。同样,这将是如何在带斜杠的URL上使用sed的答案。它没有回答OP问题,即如何转义用户输入的字符串,该字符串可以包含/,\,但是如果决定使用该字符串,还可以包含#。而且,URI也可以包含#
papo

2
它改变了我的生活!谢谢!
弗朗西斯科·桑托斯

48

在replace子句中专门处理的仅有三个文字字符是/(关闭子句),\(转义字符,后向引用&c。)和&(将匹配包括在替换中)。因此,您所需要做的就是逃避这三个字符:

sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"

例:

$ export REPLACE="'\"|\\/><&!"
$ echo fooKEYWORDbar | sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"
foo'"|\/><&!bar

我想也是换行符。我如何逃脱换行符?
亚历山大·格拉迪什

2
注意反斜杠的echo默认行为是什么。在bash中,echo默认为不对反斜杠转义进行任何解释,这在此起作用。另一方面,在破折号(sh)中,echo解释了反斜杠转义符,据我所知,没有办法抑制这种情况。因此,在破折号(sh)中,而不是echo $ x,请执行printf'%s \ n'$ x。
Youssef Eldakar

另外,在进行读取时,请始终使用-r选项将用户输入中的反斜杠视为文字。
Youssef Eldakar

为了与其他shell跨平台兼容,您应该参考有关sed特殊字符的替换的文档:grymoire.com/Unix/Sed.html#toc-uh-62
Clayton

2
@Drux这三个字符是replace子句中唯一的特殊字符。pattern子句还有很多特别之处。
lenz

33

基于Pianosaurus的正则表达式,我制作了一个bash函数,该函数同时避免了关键字和替换。

function sedeasy {
  sed -i "s/$(echo $1 | sed -e 's/\([[\/.*]\|\]\)/\\&/g')/$(echo $2 | sed -e 's/[\/&]/\\&/g')/g" $3
}

使用方法如下:

sedeasy "include /etc/nginx/conf.d/*" "include /apps/*/conf/nginx.conf" /etc/nginx/nginx.conf

3
谢谢!如果有人像我一样在尝试使用它时遇到语法错误,请记住要使用bash而不是sh来运行它
Konstantin Pereiaslov 2013年

1
是否有一个仅将字符串转义为sed而不是环绕sed的函数?
CMCDragonkai 2014年

嘿,只是关于使用回声来启动管道的一般警告,如下所示:回声的某些(大多数?)实现采用选项(请参见参考资料man echo),从而导致参数$1以破折号开头时管道的行为异常。相反,您可以使用来启动管道printf '%s\n' "$1"
侏罗纪

17

做出响应有点晚了...但是有一种更简单的方法可以执行此操作。只需更改定界符(即,分隔字段的字符)。所以,不是s/foo/bar/你写s|bar|foo

而且,这是执行此操作的简单方法:

sed 's|/\*!50017 DEFINER=`snafu`@`localhost`\*/||g'

结果输出没有那个讨厌的DEFINER子句。


10
不,&和``必须仍然转义,分隔符也必须转义,无论选择哪个。
mirabilos 2014年

3
这解决了我的问题,因为替换字符串中包含“ /”字符。谢啦!
Evgeny Goldin 2014年

为我工作。这是在尝试$在要更改的字符串中转义,并保持$替换字符串中的含义。说我想更改$XXX为variable的值$YYYsed -i "s|\$XXX|$YYY|g" file效果很好。
hakunami 2014年

11

原来你在问错问题。我也问错了问题。错误的原因是第一句话的开头:“在我的bash脚本中……”。

我有同样的问题,也犯了同样的错误。如果您使用的是bash,则无需使用sed进行字符串替换(这非常有用)使用bash内置的替换功能会清洁)。

而不是例如:

function escape-all-funny-characters() { UNKNOWN_CODE_THAT_ANSWERS_THE_QUESTION_YOU_ASKED; }
INPUT='some long string with KEYWORD that need replacing KEYWORD.'
A="$(escape-all-funny-characters 'KEYWORD')"
B="$(escape-all-funny-characters '<funny characters here>')"
OUTPUT="$(sed "s/$A/$B/g" <<<"$INPUT")"

您可以专门使用bash功能:

INPUT='some long string with KEYWORD that need replacing KEYWORD.'
A='KEYWORD'
B='<funny characters here>'
OUTPUT="${INPUT//"$A"/"$B"}"

顺便说一句,此处语法高亮显示是错误的。外部报价匹配,内部报价匹配。换句话说,它看起来像$A并且$B未引用,但事实并非如此。内部${}的引号与外部的引号不匹配。
destenson

实际上,您不必引用作业的右侧(除非您想执行var='has space'),这OUTPUT=${INPUT//"$A"/"$B"}是安全的。
本杰明W.

实际上,您不必引用作业的右侧(除非您希望它在现实世界中起作用,而不仅仅是作为玩具脚本来展示yur mad skilz)。我总是尝试引用我不希望shell解释的每个变量扩展,除非我有特定的理由不这样做。这样一来,事情往往不会经常中断,尤其是在提供新的或意外的输入时。
destenson

1
请参见手册:“所有值都经过波浪号扩展,参数和变量扩展,命令替换,算术扩展和引号删除(详细信息如下)。” 即,与双引号相同。
本杰明·

1
如果需要在文件上使用sed怎么办?
伊夫伦

1

使用awk-它更干净:

$ awk -v R='//addr:\\file' '{ sub("THIS", R, $0); print $0 }' <<< "http://file:\_THIS_/path/to/a/file\\is\\\a\\ nightmare"
http://file:\_//addr:\file_/path/to/a/file\\is\\\a\\ nightmare

2
麻烦的awk是它与几乎没有任何相似之处sed -i,它在99%的时间内非常方便。
Tino

这是朝着正确方向迈出的一步,但是awk仍然会在您的替换中解释一些元字符,因此对于用户输入而言仍然不安全。
Jeremy Huiskamp,

0

这是我前一段时间使用的AWK的示例。这是一个打印新AWKS的AWK。AWK和SED相似,这可能是一个很好的模板。

ls | awk '{ print "awk " "'"'"'"  " {print $1,$2,$3} " "'"'"'"  " " $1 ".old_ext > " $1 ".new_ext"  }' > for_the_birds

看起来有些多余,但是以某种方式使用引号组合可以使'保持原样打印。然后,如果我没记错的话,这些变量只是被这样的引号引起来:“ $ 1”。尝试一下,让我知道它如何与SED一起使用。


0

我对sedeasy函数进行了改进,该函数将以制表符之类的特殊字符中断。

function sedeasy_improved {
    sed -i "s/$(
        echo "$1" | sed -e 's/\([[\/.*]\|\]\)/\\&/g' 
            | sed -e 's:\t:\\t:g'
    )/$(
        echo "$2" | sed -e 's/[\/&]/\\&/g' 
            | sed -e 's:\t:\\t:g'
    )/g" "$3"
}

那么,有什么不同呢?$1$2用引号引起来,以避免shell扩展并保留制表符或双精度空格。

附加管道传递| sed -e 's:\t:\\t:g'(我喜欢:作为令牌),用于转换中的标签页\t


但是,请参阅我对有关在管道中使用回声的简单答案的评论。
侏罗纪

0

这些是我发现的转义代码:

* = \x2a
( = \x28
) = \x29

" = \x22
/ = \x2f
\ = \x5c

' = \x27
? = \x3f
% = \x25
^ = \x5e

-1

不要忘记围绕“和”的shell限制所带来的所有乐趣。

因此(以ksh为单位)

Var=">New version of \"content' here <"
printf "%s" "${Var}" | sed "s/[&\/\\\\*\\"']/\\&/g' | read -r EscVar

echo "Here is your \"text\" to change" | sed "s/text/${EscVar}/g"

正是我需要的方向,才能通过Google找到转义的结果,因此可能对某人有用-以-sed“ s / [&\\\ * \\” \'\“')(] / \\& /
g'– MolbOrg

-1

如果您只是想在sed命令中替换Variable value,那么只需删除Example:

sed -i 's/dev-/dev-$ENV/g' test to sed -i s/dev-/dev-$ENV/g test

-2

如果碰巧是您正在生成一个随机密码以传递以sed替换模式,那么您选择要谨慎对待随机字符串中的哪一组字符。如果您选择通过将值编码为base64制成的密码,则base64中仅存在既可以使用的字符,又是sed替换模式中的特殊字符。该字符为“ /”,很容易从生成的密码中删除:

# password 32 characters log, minus any copies of the "/" character.
pass=`openssl rand -base64 32 | sed -e 's/\///g'`;

-4

一种更简单的方法是简单地事先构建字符串并将其用作参数 sed

rpstring="s/KEYWORD/$REPLACE/g"
sed -i $rpstring  test.txt

失败且极为危险,因为由用户提供REPLACE=/了REPLACE: 提供sed: -e expression #1, char 12: unknown option to `s'
-Tino
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.