用 sed 逐行替换模式范围

Replace a pattern range line by line with sed

我需要替换按 ID 排序的模式范围的每一行的开头,我使用的是 sed,但欢迎使用其他语言!

这是包含目标的示例文本

...
==OPEN==
  data: blabla
  id: class1
  moredata: blabla
==CLOSE==

==OPEN==
  id: class2
  boringdata: blabla
==CLOSE==

...extra info

==OPEN==
  id: class8
  data: ...
==CLOSE==

...more info

==OPEN==
  data: ...
  boringdata: ...
  id: class10
==CLOSE==
...

如果我要注释掉 id 为 8 的模式,预期的输出将是:

...
==OPEN==
  data: blabla
  id: class1
  moredata: blabla
==CLOSE==

==OPEN==
  id: class2
  boringdata: blabla
==CLOSE==

...extra info

// ==OPEN==
//   id: class8
//   data: ...
// ==CLOSE==

...more info

==OPEN==
  data: ...
  boringdata: ...
  id: class10
==CLOSE==
...

我得到的最接近的代码是这样的,但我必须重写整个范围,而且价格不菲:

sed -e '/==OPEN==/ {:loop; N; /==CLOSE==/! b loop; /id: class8/ {s/.*/NEEDS REWRITE/}}' /example

如果我告诉它重写开头 (^),它只重写范围的第一行,我认为这是因为它把整个模式视为一行。

The closest code I have gotten is this, but I have to rewrite the entire range and it is not affordable:

sed -e '/==OPEN==/ {:loop; N; /==CLOSE==/! b loop; /id: class8/ {s/.*/NEEDS REWRITE/}}' /example

其实还不错。

If I tell it to rewrite the beginning (^), it rewrites only the first line of the range, I think it is because it considers the entire pattern as one line.

是的,默认情况下 POSIX sed 和 GNU sed^ 仅匹配模式 space 的开头。但是,您可以自己匹配换行符:

sed -e '/==OPEN==/ {:loop; N; /==CLOSE==/! b loop; /id: class8/ {s,\(^\|\n\),// ,g}}' \
  /example

特别注意:

  • 被替换的文本表示为组 \(^\|\n\),表示模式 space 开头的零长度子字符串或作为组捕获的换行符。
  • 匹配的文本通过 </code> 回显到替换中。当匹配 <code>^ 替代项时,这没有明显的效果,但它避免在其他替代项匹配时消除换行符。
  • 逗号(,)用作模式分隔符,因此替换文本中的斜线(/)不需要转义。
  • g 标志用于导致所有 次出现的模式被替换,而不仅仅是第一个。

如果你愿意依赖 GNU 扩展,那么你可以做的更简单一点:

sed -e '/==OPEN==/ {:loop; N; /==CLOSE==/! b loop; /id: class8/ {s,^,// ,gm}}' \
  /example

使用 GNU seds 命令中的 m 标志导致 ^ 在模式 space 的开头和立即匹配在每个换行符之后。此标志未由 POSIX.

指定