首页 > 解决方案 > 用 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 '/==打开==/ {:loop; N; /==关闭==/!b 循环;/id: class8/ {s/.*/NEEDS REWRITE/}}' /example

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

标签: linuxawksedgnubsd

解决方案


我得到的最接近的代码是这样的,但我必须重写整个范围,而且它负担不起:

sed -e '/==打开==/ {:loop; N; /==关闭==/!b 循环;/id: class8/ {s/.*/NEEDS REWRITE/}}' /example

这其实还不错。

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

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

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

特别注意:

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

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

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

对于 GNU sed,命令m中的标志会导致在模式空间的开头和每个换行符之后立即匹配。POSIX 未指定此标志。s^


推荐阅读