在 sh 脚本中使用 sed 时需要转义哪些字符?

在 sh 脚本中使用 sed 时需要转义哪些字符?

采取以下脚本:

#!/bin/sh
sed 's/(127\.0\.1\.1)\s/\1/' [some file]

如果我尝试在shdash此处)中运行它,它会因为需要转义的括号而失败。但是我需要转义反斜杠本身(在八位位组之间,或在\sor中\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\]])。

在替换文本中:

  • &\需要在它们前面加上反斜杠来引用,就像分隔符(通常/)和换行符一样。
  • \后面跟着的数字有特殊含义。\后面跟着的字母在某些实现中具有特殊含义(特殊字符),\后面跟着一些其他字符意味着\cc取决于实现。
  • 使用单引号将参数 ( 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。

总而言之,版本的差异确实可以决定正确的语法可能是什么样子,或者需要转义哪些字符。我希望这为最初的问题和已接受的答案提供了一些额外的上下文,并帮助其他人根据脚本和命令使用的最终目标考虑如何继续。

相关内容