采取以下脚本:
#!/bin/sh
sed 's/(127\.0\.1\.1)\s/\1/' [some file]
如果我尝试在sh
(dash
此处)中运行它,它会因为需要转义的括号而失败。但是我不需要转义反斜杠本身(在八位位组之间,或在\s
or中\1
)。这里有什么规则?当我需要使用{...}
or时怎么办[...]
?有没有一份清单列出了我该做什么而不需要逃避的事情?
答案1
这里有两个级别的解释:shell 和 sed。
在 shell 中,单引号之间的所有内容均按字面解释,但单引号本身除外。您可以通过编写'\''
(闭合单引号、一个文字单引号、打开单引号)有效地在单引号之间添加单引号。
Sed 用途基本正则表达式。在 BRE 中,为了按字面意思处理它们,$.*[\^
需要在字符前面加上反斜杠来引用字符,字符集 ( […]
) 内除外。字母、数字(){}+?|
不得被引用(在某些实现中,您可以引用其中的一些内容)。序列\(
、\)
、\n
、 以及在某些实现中\{
、\}
、\+
、\?
和\|
其他反斜杠+字母数字具有特殊含义。$^
在某些实现中,您可以在某些位置不引用。
/
此外,如果要出现在括号表达式之外的正则表达式中,则需要在前面添加反斜杠。您可以通过写入来选择替代字符作为分隔符,例如,s~/dir~/replacement~
或\~/dir~p
;如果您想将其包含在 BRE 中,则在分隔符之前需要有一个反斜杠。如果您选择在 BRE 中具有特殊含义的字符并且希望按字面意思包含它,则需要三个反斜杠;我不建议这样做,因为它在某些实现中可能表现不同。
简而言之,对于sed 's/…/…/'
:
- 在单引号之间写入正则表达式。
- 用于
'\''
在正则表达式中以单引号结束。 - 在这些字符之前且仅在这些字符之前添加反斜杠
$.*/[\]^
(但不在括号表达式内)。 (从技术上讲,您不应该在前面添加反斜杠]
,但我不知道有什么实现可以在括号表达式之外以不同方式处理]
和。)\]
- 在括号表达式内,为了按
-
字面意思处理,请确保它是第一个或最后一个([abc-]
或[-abc]
,不是)。[a-bc]
- 在括号表达式内,为了
^
按字面意思处理,请确保它是不是首先(使用[abc^]
,不使用)。[^abc]
- 要包含
]
在与方括号表达式匹配的字符列表中,请将其作为第一个字符(或者^
对于否定集,将其作为第一个字符):[]abc]
or[^]abc]
(not)。[abc]]
或[abc\]]
在替换文本中:
&
并\
需要在它们前面加上反斜杠来引用,就像分隔符(通常/
)和换行符一样。\
后面跟着的数字有特殊含义。\
后面跟着的字母在某些实现中具有特殊含义(特殊字符),\
后面跟着一些其他字符意味着\c
或c
取决于实现。- 使用单引号将参数 (
sed 's/…/…/'
) 括起来,用于'\''
在替换文本中添加单引号。
如果正则表达式或替换文本来自 shell 变量,请记住
- 正则表达式是 BRE,而不是文字字符串。
- 在正则表达式中,换行符需要表示为(除非您有其他代码将换行符添加到模式空间,否则
\n
它永远不会匹配)。sed
但请注意,它在某些实现的括号表达式内不起作用sed
。 - 在替换文本中,
&
、\
和换行符需要被引用。 - 分隔符需要加引号(但不在括号表达式内)。
- 使用双引号进行插值:
sed -e "s/$BRE/$REPL/"
.
答案2
您遇到的问题不是由于 shell 插值和转义造成的 - 这是因为您尝试使用扩展正则表达式语法而不传递 sed-r
或--regexp-extended
选项。
更改您的 sed 行
sed 's/(127\.0\.1\.1)\s/\1/' [some file]
到
sed -r 's/(127\.0\.1\.1)\s/\1/' [some file]
我相信它会按照你的意图工作。
默认情况下 sed 使用基本正则表达式(想想 grep 样式),这需要以下语法:
sed 's/\(127\.0\.1\.1\)[ \t]/\1/' [some file]
答案3
除非您想将 shell 变量插入到 sed 表达式中,否则请对整个表达式使用单引号,因为它们会导致它们之间的所有内容都按原样解释,包括反斜杠。
因此,如果您希望 sed 看到,s/\(127\.0\.1\.1\)\s/\1/
请在其周围加上单引号,并且 shell 不会触及其中的括号或反斜杠。如果需要插入 shell 变量,只需将该部分放在双引号中。例如
sed 's/\(127\.0\.1\.1\)/'"$ip"'/'
这将为您省去记住哪些 shell 元字符不使用双引号转义的麻烦。
答案4
我认为值得一提的是,虽然 sed 基于 POSIX 标准,该标准指定仅支持基本正则表达式(BRE),但实际上存在两个不同版本的 sed 命令 - BSD(Mac OS)和 GNU(Linux 发行版) 。每个版本都实现了类似且独特的 POSIX 标准扩展,并且可以影响 sed 跨不同平台的功能。因此,sed 命令的正确语法在一个系统上按预期运行,实际上可能会在另一个系统上转换为完全不同的结果。这可能会导致在使用转义字符和特殊字符方面出现意外行为。
这些对 POSIX 标准的扩展在 sed 的 GNU 版本上往往更为普遍,通常提供不太严格的格式设置的便利,特别是与 BSD 版本相比。然而,虽然 GNU sed 确实允许某些特殊字符的功能,但它们实际上仍然不符合 POSIX 标准。此外,GNU sed 中基本正则表达式和扩展正则表达式 (ERE) 之间的唯一真正区别是以下特殊字符的行为:
'?'、'+'、圆括号、大括号 ('{}') 和 '|'
尽管情况可能如此,但某些特殊字符在 BSD sed 上的支持有限或根本不支持,例如“|”、“?”和“+”,因为它更严格地遵守 POSIX 语法标准。以类似于 GNU sed 的方式包含这些字符,通常会导致使用 sed 的脚本的可移植性和功能性问题。还值得注意的是,POSIX BRE 语法没有定义某些转义序列的含义,最值得注意的是:\|、+、\?、`、\'、\<、>、\b、\B、\w 和 \瓦,。
对于那些运行 BSD/Mac OS 版本的 sed 的人来说,模拟某些特殊字符的行为可能有点棘手,但在大多数情况下都是可以完成的。例如,+ 可以按照 POSIX 兼容的方式进行模拟,如下所示:{1,} 和 \?看起来像这样: {0,1} 但是,通常不支持控制字符序列。如果可能的话,使用 GNU sed 当然是最简单的,但如果您需要两个平台上的功能,请记住仅使用 POSIX 功能,以确保可移植性。如果您是 Mac 用户并且希望利用 GNU sed 而不是 BSD sed,您可以尝试安装 Homebrew,并通过命令行下载 GNU sed:$brew install gnu-sed。
总而言之,版本的差异确实可以决定正确的语法可能是什么样子,或者需要转义哪些字符。我希望这为最初的问题和已接受的答案提供了一些额外的上下文,并帮助其他人根据脚本和命令使用的最终目标考虑如何继续。