Note:
单线解决方案
转义字符串文字以用作regex in sed
:
To give credit where credit is due: I found the regex used below in this answer.
假设搜索字符串是single-行字符串:
search='abc\n\t[a-z]\+\([^ ]\)\{2,3\}\3' # sample input containing metachars.
searchEscaped=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$search") # escape it.
sed -n "s/$searchEscaped/foo/p" <<<"$search" # Echoes 'foo'
- Every character except
^
is placed in its own character set [...]
expression to treat it as a literal.
- 注意
^
是一个字符。你cannot表示为[^]
,因为它在该位置具有特殊含义(否定)。
- Then,
^
chars. are escaped as \^
.
- 请注意,您不能通过放置一个来转义每个字符
\
在它前面,因为这可以将文字字符转换为元字符,例如\<
and \b
是某些工具中的单词边界,\n
是一个换行符,\{
是 RE 间隔的开始,例如\{1,3\}
, etc.
该方法很稳健,但效率不高。
The 鲁棒性来自于not尝试预测所有特殊的正则表达式字符- 这会因正则表达式方言而异 - 但只关注 2 个功能所有正则表达式方言共享:
- 指定字符集中文字字符的能力。
- 逃避字面意义的能力
^
as \^
转义字符串文字以用作替换字符串 in sed
's s///
命令:
a 中的替换字符串sed
s///
命令不是正则表达式,但它可以识别占位符引用正则表达式匹配的整个字符串(&
) 或按索引 (\1
, \2
, ...),因此必须将它们与(习惯的)正则表达式分隔符一起转义,/
.
假设替换字符串是single-行字符串:
replace='Laurel & Hardy; PS\2' # sample input containing metachars.
replaceEscaped=$(sed 's/[&/\]/\\&/g' <<<"$replace") # escape it
sed -n "s/.*/$replaceEscaped/p" <<<"foo" # Echoes $replace as-is
多线解决方案
转义多行字符串文字以用作regex in sed
:
Note:这只有在以下情况下才有意义多条输入线(可能是全部)在尝试匹配之前已被读取。
由于诸如sed
and awk
操作于single默认情况下一次读取一行,需要额外的步骤才能使它们一次读取多行。
# Define sample multi-line literal.
search='/abc\n\t[a-z]\+\([^ ]\)\{2,3\}\3
/def\n\t[A-Z]\+\([^ ]\)\{3,4\}\4'
# Escape it.
searchEscaped=$(sed -e 's/[^^]/[&]/g; s/\^/\\^/g; $!a\'$'\n''\\n' <<<"$search" | tr -d '\n') #'
# Use in a Sed command that reads ALL input lines up front.
# If ok, echoes 'foo'
sed -n -e ':a' -e '$!{N;ba' -e '}' -e "s/$searchEscaped/foo/p" <<<"$search"
- 多行输入字符串中的换行符必须转换为
'\n'
strings,这就是正则表达式中换行符的编码方式。
-
$!a\'$'\n''\\n'
追加string '\n'
除了最后一行之外的每个输出行(最后一个换行符被忽略,因为它是由<<<
)
-
tr -d '\n
然后删除所有actual字符串中的换行符 (sed
每当打印其模式空间时就添加一个),有效地将输入中的所有换行符替换为'\n'
字符串。
转义多行字符串文字以用作替换字符串 in sed
's s///
命令:
# Define sample multi-line literal.
replace='Laurel & Hardy; PS\2
Masters\1 & Johnson\2'
# Escape it for use as a Sed replacement string.
IFS= read -d '' -r < <(sed -e ':a' -e '$!{N;ba' -e '}' -e 's/[&/\]/\\&/g; s/\n/\\&/g' <<<"$replace")
replaceEscaped=${REPLY%$'\n'}
# If ok, outputs $replace as is.
sed -n "s/\(.*\) \(.*\)/$replaceEscaped/p" <<<"foo bar"
- 输入字符串中的换行符必须保留为实际换行符,但是
\
-逃脱了。
-
-e ':a' -e '$!{N;ba' -e '}'
是符合 POSIX 标准的形式sed
读着的成语all输入线循环。
-
's/[&/\]/\\&/g
逃脱所有&
, \
and /
实例,如单行解决方案中一样。
-
s/\n/\\&/g'
then \
-为所有实际换行添加前缀。
-
IFS= read -d '' -r
用于读取sed
命令的输出as is(以避免自动删除命令替换($(...)
)将执行)。
-
${REPLY%$'\n'}
然后删除一个single尾随换行符,其中<<<
已隐式附加到输入。
bash
功能基于上述(对于sed
):
-
quoteRe()
用于在 a 中使用的引号(转义符)regex
-
quoteSubst()
引用用于替换字符串 of a s///
call.
- both handle multi-line input correctly
- 请注意,因为
sed
读到single默认情况下,在某个时间行,使用quoteRe()
多行字符串仅在以下情况下才有意义sed
一次显式读取多行(或全部)行的命令。
- 另外,使用命令替换(
$(...)
)调用函数对于具有以下内容的字符串不起作用trailing换行符;在这种情况下,使用类似的东西IFS= read -d '' -r escapedValue <(quoteSubst "$value")
# SYNOPSIS
# quoteRe <text>
quoteRe() { sed -e 's/[^^]/[&]/g; s/\^/\\^/g; $!a\'$'\n''\\n' <<<"$1" | tr -d '\n'; }
# SYNOPSIS
# quoteSubst <text>
quoteSubst() {
IFS= read -d '' -r < <(sed -e ':a' -e '$!{N;ba' -e '}' -e 's/[&/\]/\\&/g; s/\n/\\&/g' <<<"$1")
printf %s "${REPLY%$'\n'}"
}
Example:
from=$'Cost\(*):\n$3.' # sample input containing metachars.
to='You & I'$'\n''eating A\1 sauce.' # sample replacement string with metachars.
# Should print the unmodified value of $to
sed -e ':a' -e '$!{N;ba' -e '}' -e "s/$(quoteRe "$from")/$(quoteSubst "$to")/" <<<"$from"
注意使用-e ':a' -e '$!{N;ba' -e '}'
一次读取所有输入,以便多行替换起作用。
perl
解决方案:
Perl 具有内置支持用于转义任意字符串以供正则表达式中的文字使用:quotemeta()功能或其等价物\Q...\E
quoting.
对于单行和多行字符串,该方法是相同的;例如:
from=$'Cost\(*):\n$3.' # sample input containing metachars.
to='You owe me $1/$& for'$'\n''eating A\1 sauce.' # sample replacement string w/ metachars.
# Should print the unmodified value of $to.
# Note that the replacement value needs NO escaping.
perl -s -0777 -pe 's/\Q$from\E/$to/' -- -from="$from" -to="$to" <<<"$from"