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

det*_*tly 346 sed shell-script quoting

执行以下脚本:

#!/bin/sh
sed 's/(127\.0\.1\.1)\s/\1/' [some file]
Run Code Online (Sandbox Code Playgroud)

如果我尝试在sh( dashhere) 中运行它,它会因为需要转义的括号而失败。但是,我并不需要转义反斜线本身(八位位组之间,或\s\1)。这里的规则是什么?当我需要使用{...}或 时[...]呢?有没有我做什么和不需要逃避的清单?

Gil*_*il' 393

这里有两个级别的解释:shell 和 sed。

在 shell 中,单引号之间的所有内容都按字面解释,单引号本身除外。您可以通过编写'\''(关闭单引号,一个文字单引号,打开单引号)有效地在单引号之间使用单引号。

Sed 使用基本的正则表达式。在 BRE 中,为了按字面处理它们,$.*[\^需要在字符前面加上反斜杠来引用字符,除了在字符集 ( […]) 中。字母、数字和(){}+?|不能被引用(在某些实现中你可以避免引用其中的一些)。序列\(\)\n和在某些实现\{\}\+\?\|和其他反斜杠+字母数字具有特殊含义。$^在某些实现中,您可以避免在某些位置引用。

此外,/如果要出现在方括号表达式之外的正则表达式中,则在此之前需要一个反斜杠。您可以选择一个替代字符作为分隔符,例如,s~/dir~/replacement~\~/dir~p;如果要将其包含在 BRE 中,则在分隔符之前需要一个反斜杠。如果您选择一个在 BRE 中具有特殊含义的字符并且您想按字面意思包含它,则需要三个反斜杠;我不推荐这样做,因为它在某些实现中的行为可能不同。

简而言之,对于sed 's/…/…/'

  • 在单引号之间写正则表达式。
  • 用于'\''在正则表达式中以单引号结尾。
  • $.*/[\]^这些字符之前加上反斜杠(但不在括号表达式内)。(从技术上讲,你不应该在前面加上反斜杠,]但我不知道在括号表达式之外处理]\]不同的实现。)
  • 在括号表达式中,-要按字面意思处理,请确保它是第一个或最后一个([abc-][-abc],不是[a-bc])。
  • 在括号表达式中,^要按字面意思处理,请确保它不是第一个(使用[abc^],而不是[^abc])。
  • 要包含]在由括号表达式匹配的字符列表中,请将其^设为第一个字符(或否定集合的第一个字符):[]abc][^]abc](非[abc]][abc\]])。

在替换文本中:

  • &并且\需要通过在它们前面加上反斜杠来引用,分隔符(通常是/)和换行符也是如此。
  • \后跟一个数字有特殊含义。\后跟字母在某些实现中具有特殊含义(特殊字符),\后跟一些其他字符意味着\cc取决于实现。
  • 在参数 ( sed 's/…/…/')周围使用单引号,用于'\''在替换文本中放置单引号。

如果正则表达式或替换文本来自 shell 变量,请记住

  • 正则表达式是 BRE,而不是文字字符串。
  • 在正则表达式中,换行符需要表示为\n(除非您有其他sed代码将换行符添加到模式空间,否则永远不会匹配)。但请注意,它在某些sed实现的括号表达式中不起作用。
  • 在替换文本中,需要引用&,\和换行符。
  • 分隔符需要被引用(但不在括号表达式内)。
  • 使用双引号进行插值:sed -e "s/$BRE/$REPL/".

  • 我也必须转义 `+`,因为它具有正则表达式的含义。 (2认同)

R P*_*rin 51

您遇到的问题不是由于 shell 插值和转义 - 这是因为您试图使用扩展的正则表达式语法而不传递 sed-r--regexp-extended选项。

将您的 sed 行从

sed 's/(127\.0\.1\.1)\s/\1/' [some file]
Run Code Online (Sandbox Code Playgroud)

sed -r 's/(127\.0\.1\.1)\s/\1/' [some file]
Run Code Online (Sandbox Code Playgroud)

我相信它会按你的意愿工作。

默认情况下 sed 使用基本的正则表达式(想想 grep 风格),这需要以下语法:

sed 's/\(127\.0\.1\.1\)[ \t]/\1/' [some file]
Run Code Online (Sandbox Code Playgroud)

  • @HubertGrzeskowiak 在 MacOS 上使用 `-E` 与 `-r` 的技巧相同 [see](https://unix.stackexchange.com/a/131940) (2认同)

Kyl*_*nes 20

除非您想将 shell 变量插入到 sed 表达式中,否则请对整个表达式使用单引号,因为它们会导致它们之间的所有内容都按原样解释,包括反斜杠。

因此,如果您希望 sed 看到s/\(127\.0\.1\.1\)\s/\1/在它周围加上单引号,并且 shell 不会触及其中的括号或反斜杠。如果您需要插入一个 shell 变量,只需将该部分放在双引号中。例如

sed 's/\(127\.0\.1\.1\)/'"$ip"'/'
Run Code Online (Sandbox Code Playgroud)

这将为您省去记住哪些 shell 元字符没有被双引号转义的麻烦。

  • 外壳不接触括号。您需要反斜杠,因为 **sed** 需要看到它们。`sed 's/(127\.0\.1\.1)/IP \1/'` 失败,因为 sed 需要查看组语法的 `\(` 和 `\)`,而不是 `(` 和 `) `. (3认同)
  • 对于传统的 Unix 命令,有基本的正则表达式和扩展的正则表达式。[详情](http://en.wikipedia.org/wiki/Regular_expression#POSIX_Basic_Regular_Expressions)。sed 使用基本的正则表达式,因此组语法需要反斜杠。Perl 和 Python 甚至超越了扩展的正则表达式。当我四处闲逛时,我发现了一个[信息量极大的图表](http://www.regular-expressions.info/refflavors.html),它说明了当我们口齿不清地说“正则表达式”时,我们会联想到多么令人困惑的荆棘。 (3认同)