题
在我的bash脚本中,我有一个外部(从用户接收)字符串,我应该在sed模式中使用。
REPLACE="<funny characters here>"
sed "s/KEYWORD/$REPLACE/g"
如何转义 $ REPLACE
字符串,以便 sed
安全地接受它作为文字替换?
注意: KEYWORD
是一个没有匹配等的哑子字符串。它不是由用户提供的。
解决方案
警告:这不考虑换行。有关更深入的答案,请参阅这个SO问题来可靠地逃避正则表达式元字符。 (谢谢,Ed Morton和Niklas Peter)
请注意,逃避一切都是个坏主意。 Sed需要将许多字符转义为获取其特殊含义。例如,如果您转义替换字符串中的数字,它将转入反向引用。
正如Ben Blank所说,在替换字符串中只需要转义三个字符(自行转义,转发语句结束正斜杠和&amp;替换全部):
sed -e 's/[\/&]/\\&/g'
如果您需要转义 KEYWORD
字符串,则需要以下内容:
sed -e 's/[]\/$*.^[]/\\&/g'
请记住,如果您使用 /
以外的字符作为分隔符,则需要使用您正在使用的字符替换上面表达式中的斜杠。请参阅PeterJCLaw的评论以获得解释。
已编辑:由于之前未考虑的某些角落情况,上述命令已多次更改。查看编辑历史记录以获取详细信息。
其他提示
sed命令允许您使用其他字符而不是 /
作为分隔符:
sed 's#"http://www\.fubar\.com"#URL_FUBAR#g'
双引号不是问题。
在replace子句中专门处理的唯一三个文字字符是 /
(用于关闭子句), \
(用于转义字符,反向引用和&amp; c) 。)和&amp;
(在替换中包含匹配)。因此,您需要做的就是逃避这三个字符:
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
基于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
回复有点迟了......但是有一个更简单的方法可以做到这一点。只需更改分隔符(即分隔字段的字符)。因此,您可以编写 s | bar | foo
而不是 s / foo / bar /
。
而且,这是简单的方法:
sed 's|/\*!50017 DEFINER=`snafu`@`localhost`\*/||g'
结果输出没有那个令人讨厌的DEFINER子句。
事实证明你提出了错误的问题。我也问了一个错误的问题。它错误的原因是第一句话的开头:“在我的 bash 脚本中......”。
我有同样的问题&amp;犯了同样的错误。如果你正在使用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"}"
使用awk - 它更干净:
$ awk -v R='//addr:\\file' '{ sub("THIS", R, <*>); print <*> }' <<< "http://file:\_THIS_/path/to/a/file\\is\\\a\\ nightmare"
http://file:\_//addr:\file_/path/to/a/file\\is\\\a\\ nightmare
这是我前一段时间使用的AWK示例。这是一款打印新AWKS的AWK。 AWK和SED相似,可能是一个很好的模板。
ls | awk '{ print "awk " "'"'"'" " {print $1,$2,$3} " "'"'"'" " " $1 ".old_ext > " $1 ".new_ext" }' > for_the_birds
它看起来过分,但不知何故,引号的组合有助于保持'打印为文字。然后,如果我没记错的话,vaiables只是用这样的引号包围:“$ 1”。试试吧,让我知道它如何与SED一起使用。
我对sedeasy功能有了改进,它会突破像tab这样的特殊字符。
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:\ t:\\ t:g'
(我喜欢:
作为标记),它转换了 \ t
中的标签。
不要忘记围绕“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"
如果情况恰好是您生成随机密码以传递给 sed
替换模式,那么您选择要小心随机字符串中的哪组字符。如果您选择通过将值编码为base64而生成的密码,那么在base64中只有可能的字符,并且在 sed
替换模式中也是一个特殊字符。该字符为“/”,可以轻松地从您生成的密码中删除:
# password 32 characters log, minus any copies of the "/" character.
pass=`openssl rand -base64 32 | sed -e 's/\///g'`;
如果您只是想在sed命令中替换Variable值,那么只需删除即可 例如:
sed -i 's/dev-/dev-$ENV/g' test to sed -i s/dev-/dev-$ENV/g test
更简单的方法是手工构建字符串并将其用作 sed
rpstring="s/KEYWORD/$REPLACE/g"
sed -i $rpstring test.txt