在对这个问题的评论中,出现了一个案例,其中各种sed实现在一个相当简单的程序上存在分歧,而我们(或至少我)无法确定规范对它的实际要求。
问题是从删除行开始的范围的行为:
1d;1,2d
即使在到达该命令之前删除了范围的开头,也应该删除第2行吗?我最初的期望是与BSD sed一致,“否”,而GNU sed说“是”,并且检查规范文本并不能完全解决问题。
符合我期望的是(至少)macOS和Solaris sed
以及BSD sed
。(至少)GNU和Busybox意见不同sed
,这里有很多人。前两个通过SUS认证,而其他两个可能更广泛。哪种行为是正确的?
两个地址范围的规范文本显示:
然后,sed实用程序应按顺序应用其地址选择该模式空间的所有命令,直到命令开始下一个周期或退出为止。
和
具有两个地址的编辑命令应从与第一个地址匹配的第一个模式空间到与第二个地址匹配的下一个模式空间选择包含范围。[...]从选定范围后的第一行开始,sed将再次寻找第一个地址。此后,应重复该过程。
可以说,第2行是 内,不管开始点是否已删除“从通过相匹配的第二下一图案空间匹配的第一个地址中的第一图案空间包容范围”。另一方面,我希望第一个d
继续进行下一个周期,而不给该范围一个开始的机会。UNIX™认证的实现可以实现我期望的功能,但可能不会达到规范的要求。
一些说明性的实验遵循,但关键的问题是:什么应该 sed
在范围上已删除的行开始呢?
实验与范例
这个问题的简化展示是,它打印行的额外副本而不是删除它们:
printf 'a\nb\n' | sed -e '1d;1,2p'
这提供sed
了两行输入,a
和b
。该程序执行两件事:
用删除第一行
1d
。该d
命令将删除模式空间并开始下一个循环。和
- 从1到2中选择行范围,并明确打印出来,除了自动打印每行接收到的内容。因此,该范围内的一行应出现两次。
我的期望是应该打印
b
仅,由于不应用该范围,因为1,2
在第1行期间从未达到该范围(因为已经d
跳至下一个循环/行),因此范围内的包含从未被开始,a
而已被删除。通常,sed
与macOS和Solaris 10 兼容的Unix会产生此输出,与sed
Solaris和BSD sed
中的非POSIX一样。
另一方面,GNU sed打印
b
b
表示它已经解释了范围。这在POSIX模式下都不会发生。Busybox的sed具有相同的行为(但并非总是相同的行为,因此它似乎不是共享代码的结果)。
进一步的实验
printf 'a\nb\nc\nd\ne\n' | sed -e '2d;2,/c/p'
printf 'a\nb\nc\nd\ne\n' | sed -e '2d;2,/d/p'
发现似乎将以删除行开始的范围视为以下一行开始的范围。这是可见的,因为/c/
不匹配以结束范围。使用/b/
启动范围并没有表现一样2
。
我使用的最初的工作示例是
printf '%s\n' a b c d e | sed -e '1{/a/d;};1,//d'
作为删除直到第一个/a/
匹配项的所有行的一种方法,即使该行位于第一行(GNU sed会使用它0,/a/d
—这是尝试与POSIX兼容的格式)。
有人认为,这应该不是删除到第二的比赛/a/
,如果第一线匹配(或整个文件,如果没有第二场比赛),这似乎是合理的-但是,只有GNU sed的做到这一点。macOS sed和Solaris的sed生产
b
c
d
e
为此,正如我所期望的那样(GNU sed通过删除未终止的范围而产生空输出; Busybox sed只打印d
和e
,无论如何显然是错误的)。通常,我认为他们已经通过了认证一致性测试意味着他们的行为是正确的,但是足够多的人建议否则我不确定,规范文本并不完全令人信服,并且测试套件不能全面。
显然,鉴于存在不一致的情况,今天编写该代码实际上并不便于移植,但从理论上讲,它在任何具有一种或另一种含义的地方都应等效。我认为这是一个错误,但我不知道针对哪个实现进行报告。我目前的看法是GNU和Busybox sed的行为与规范不一致,但是我可能会误认为。
POSIX在这里需要什么?
ed
,sed
完全绕开吗?