“sed”中的range-operator实际上做了什么,是否在GNU / busybox中被破坏了?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了“sed”中的range-operator实际上做了什么,是否在GNU / busybox中被破坏了?相关的知识,希望对你有一定的参考价值。

我想知道“sed”的GNU和BusyBox实现是否可能被破坏。

我的默认sed实现是来自GNU的实现。

POSIX说:

具有两个地址的编辑命令将从与第一个地址匹配的第一个模式空间到与第二个匹配的下一个模式空间中选择包含范围。

但是为什么要给出

$ { echo ha; echo ha; echo ha; } | sed '0,/ha/ !d'
ha

代替

ha
ha

?显然,这里的第二个“ha”是匹配的“下一个”模式空间,所以它也应该输出!

但更奇怪的是,

$ { echo ha; echo ha; echo ha; } | busybox sed '0,/ha/ !d'

根本不输出任何东西!

但即使sed会执行POSIX定义所说的内容,仍然不清楚实际检查范围表达式时会发生什么。

每个范围条件都有自己的内部状态吗?或者sed脚本中是否存在所有范围条件的单一全局状态?

显然,范围条件至少需要记住它当前是在“搜索第一个地址的匹配”状态还是在“搜索第二个地址的匹配”状态中。也许它甚至需要记住第三个状态“我已经处理了范围并且不再匹配,无论如何”。

当这些条件更新时,这当然很重要:每次读取新的模式空间时?每次修改模式空间时,比如s命令?或者只是控制流量达到范围条件?

那么,它是什么?

在我了解得更清楚之前,我将避免使用sed脚本中的范围条件,并认为它们是一个可疑的功能。

答案

两个答案:

  1. 0不是有效的POSIX地址(行数从1开始)
  2. 0,/re/是GNU扩展

GNU awk手册页包括:

0,ADDR2

从“匹配的第一个地址”状态开始,直到找到addr2。这与1,addr2类似,不同之处在于如果addr2匹配第一行输入0,则addr2形式将位于其范围的末尾,而1,addr2形式仍将位于其范围的开头。仅当addr2是正则表达式时,此方法才有效。

也许这有助于澄清:

$ { echo ha1; echo ha2; echo ha3; } | sed '0,/ha/ !d'
ha1

$ { echo ha1; echo ha2; echo ha3; } | sed '1,/ha/ !d'
ha1
ha2

$ { echo ha1; echo ha2; echo ha3; } | sed --posix '0,/ha/ !d'
sed: -e expression #1, char 8: invalid usage of line address 0

busybox代码显式检查addr1大于0,因此永远不会进入匹配状态。见the busybox source code, line 1121

            || (sed_cmd->beg_line > 0

  1. 每个匹配都保持自己的状态,因为多个匹配可以同时激活。

POSIX说:

具有两个地址的编辑命令将从与第一个地址匹配的第一个模式空间到与第二个匹配的下一个模式空间中选择包含范围。 (如果第二个地址是一个小于或等于首先选择的行号的数字,则只选择一行。)从所选范围之后的第一行开始,sed将再次查看第一个地址。此后,应重复该过程。

每次遇到测试时都会进行测试:

$ { echo ..a; echo ..b; echo ..c; } |
  sed -n '
             =;
             y/cba/ba:/;
     1 ,/b/  s/$/ 1/p;
    /a/,/c/  s/$/ 2/p;
     2,  3   s/$/ 3/p;
  '
1
..: 1
2
..a 1
..a 1 2
..a 1 2 3
3
..b 1
..b 1 2
..b 1 2 3

例如,the busybox source code也证明了这一点 - 参见sed_cmd_s typedef。

以上是关于“sed”中的range-operator实际上做了什么,是否在GNU / busybox中被破坏了?的主要内容,如果未能解决你的问题,请参考以下文章

使用 sed 替换大量文件中的 Windows 换行符 - 但它没有

通过管道搜索模式到 Sed 中删除文件中的行

sed 编辑文件到位

shell编程中的sed问题

sed命令详解

如何用sed命令替换一行中的某个字符串