注意:
单线解决方案
转义字符串文字以用作sed中的正则表达式:
在应得的情况下给予信用:我在this answer中找到了下面使用的正则表达式。
假设搜索字符串是一个单行行的字符串:
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" # if ok, echoes 'foo'
- 除
^ 之外的每个字符都放在其自己的字符集[...] 表达式中,以将其视为文字。
- 请注意,
^ 是一个字符。你不能代表[^],因为它在那个位置有特殊的意义(否定)。
- 然后,
^ 字符。转义为\^。
- 请注意,您不能通过在每个字符前面放置
\ 来逃避每个字符,因为这会将文字字符转换为元字符,例如\< 和 \b 在某些工具中是单词边界,\n 是换行符,\{ 是 RE 间隔的开始,例如 \{1,3\},等等。
该方法稳健,但效率不高。
稳健性来自于不试图预测所有特殊的正则表达式字符——这会因正则表达式方言而异——而是只关注两个特性由所有正则表达式方言共享:
- 能够在字符集中指定文字字符。
- 能够将文字
^ 转义为\^
转义字符串文字以用作sed 的s/// 命令中的替换字符串:
seds/// 命令中的替换字符串不是正则表达式,但它可以识别 占位符 指的是正则表达式匹配的整个字符串 (&) 或特定捕获- 按索引(\1、\2、...)对结果进行分组,因此必须对这些结果以及(习惯)正则表达式分隔符 / 进行转义。
假设替换字符串是一个单行行的字符串:
replace='Laurel & Hardy; PS\2' # sample input containing metachars.
replaceEscaped=$(sed 's/[&/\]/\\&/g' <<<"$replace") # escape it
sed -n "s/\(.*\) \(.*\)/$replaceEscaped/p" <<<"foo bar" # if ok, outputs $replace as is
多线解决方案
转义多行字符串文字以用作sed 中的正则表达式:
注意:只有在尝试匹配之前已读取多个输入行(可能是全部)时,这才有意义。
由于sed 和awk 等工具默认一次只在一行 上运行,因此需要额外的步骤才能使它们一次读取多行。
# 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 然后从字符串中删除所有实际 换行符(sed 在打印其模式空间时添加一个),有效地将输入中的所有换行符替换为'\n' 字符串。
转义多行字符串文字以用作sed 的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"
- 输入字符串中的换行符必须保留为实际的换行符,但
\-escaped。
-
-e ':a' -e '$!{N;ba' -e '}' 是符合 POSIX 标准的 sed 习惯用法,它会循环读取 所有 输入行。
-
's/[&/\]/\\&/g 转义所有 &、\ 和 / 实例,就像在单行解决方案中一样。
-
s/\n/\\&/g' 然后 \-为所有实际换行添加前缀。
-
IFS= read -d '' -r 用于按原样读取sed 命令的输出(以避免自动删除命令替换 ($(...)) 将执行的尾随换行符)。
-
${REPLY%$'\n'} 然后删除 单个 尾随换行符,<<< 已隐式附加到输入。
bash 函数 基于上述(对于sed):
-
quoteRe() 引号(转义)用于 regex
-
quoteSubst() 引号用于s/// 调用的替换字符串。
- 两者都能正确处理多行输入
- 请注意,因为
sed 默认读取单 行,所以将quoteRe() 与多行字符串一起使用仅在sed 显式读取多个(或all) 行。
- 此外,使用命令替换 (
$(...)) 调用函数对于具有 尾随 换行符的字符串不起作用;在这种情况下,使用类似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'}"
}
示例:
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() function 或其等效的\Q...\E 引用。
单行字符串和多行字符串的方法相同;例如:
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"