1

我想知道“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 脚本中使用范围条件,并认为它们是一个可疑的功能。

4

1 回答 1

3

两个答案:

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

GNU awk 手册页包括:

0,地址2

从“匹配的第一个地址”状态开始,直到找到 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,因此永远不会进入匹配状态。请参阅busybox 源代码,第 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

例如,busybox 源代码也证明了这一点- 请参阅sed_cmd_stypedef。

于 2019-03-27T05:20:29.127 回答