POSIX sed对于地址范围从已删除的行开始的“ 1d; 1,2d”有什么要求?


11

在对这个问题的评论中,出现了一个案例,其中各种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了两行输入,ab。该程序执行两件事:

  1. 用删除第一行1d。该d命令

    删除模式空间并开始下一个循环。和

  2. 从1到2中选择行范围,并明确打印出来,除了自动打印每行接收到的内容。因此,该范围内的一行应出现两次。

我的期望是应该打印

b

仅,由于不应用该范围,因为1,2在第1行期间从未达到该范围(因为已经d跳至下一个循环/行),因此范围内的包含从未被开始,a而已被删除。通常,sed与macOS和Solaris 10 兼容的Unix会产生此输出,与sedSolaris和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只打印de,无论如何显然是错误的)。通常,我认为他们已经通过了认证一致性测试意味着他们的行为是正确的,但是足够多的人建议否则我不确定,规范文本并不完全令人信服,并且测试套件不能全面。

显然,鉴于存在不一致的情况,今天编写该代码实际上并不便于移植,但从理论上讲,它在任何具有一种或另一种含义的地方都应等效。我认为这是一个错误,但我不知道针对哪个实现进行报告。我目前的看法是GNU和Busybox sed的行为与规范不一致,但是我可能会误认为。

POSIX在这里需要什么?


作为临时的解决方法,写入临时文件并使用POSIX处理它edsed完全绕开吗?
D. Ben Knoble

Answers:


9

这是在2012年3月的奥斯汀小组邮件列表中提出的。这是最后的信息(奥斯丁小组的成员Geoff Clare(维护POSIX的机构),也是提出该问题的人之一。这里是从gmane NNTP接口复制的:

Date: Fri, 16 Mar 2012 17:09:42 +0000
From: Geoff Clare <gwc-7882/jkIBncuagvECLh61g@public.gmane.org>
To: austin-group-l-7882/jkIBncuagvECLh61g@public.gmane.org
Newsgroups: gmane.comp.standards.posix.austin.general
Subject: Re: Strange addressing issue in sed

Stephane Chazelas <stephane_chazelas-Qt13gs6zZMY@public.gmane.org> wrote, on 16 Mar 2012:
>
> 2012-03-16 15:44:35 +0000, Geoff Clare:
> > I've been alerted to an odd behaviour of sed on certified UNIX
> > systems that doesn't seem to match the requirements of the
> > standard.  It concerns an interaction between the 'n' command
> > and address matching.
> > 
> > According to the standard, this command:
> > 
> > printf 'A\nB\nC\nD\n' | sed '1,3s/A/B/;1,3n;1,3s/B/C/'
> > 
> > should produce the output:
> > 
> > B
> > C
> > C
> > D
> > 
> > GNU sed does produce this, but certified UNIX systems produce this:
> > 
> > B
> > B
> > C
> > D
> > 
> > However, if I change the 1,3s/B/C/ to 2,3s/B/C/ then they produce
> > the expected output (tested on Solaris and HP-UX).
> > 
> > Is this just an obscure bug from common ancestor code, or is there
> > some legitimate reason why this address change alters the behaviour?
> [...]
> 
> I suppose the idea is that for the second 1,3cmd, line "1" has
> not been seen, so the 1,3 range is not entered.

Ah yes, now it makes sense, and it looks like the standard does
require this slightly strange behaviour, given how the processing
of the "two addresses" case is specified:

    An editing command with two addresses shall select the inclusive
    range from the first pattern space that matches the first address
    through the next pattern space that matches the second.  (If the
    second address is a number less than or equal to the line number
    first selected, only one line shall be selected.) Starting at the
    first line following the selected range, sed shall look again for
    the first address. Thereafter, the process shall be repeated.

It's specified this way because the addresses can be BREs, but if
the same matching process is applied to the line numbers (even though
they can only match at most once), then the 1,3 range on that last
command is never entered.

-- 
Geoff Clare <g.clare-7882/jkIBncuagvECLh61g@public.gmane.org>
The Open Group, Apex Plaza, Forbury Road, Reading, RG1 1AX, England

这是Geoff引用的消息其余部分(由我本人)的相关部分:

I suppose the idea is that for the second 1,3cmd, line "1" has
not been seen, so the 1,3 range is not entered.

Same idea as in

printf '%s\n' A B C | sed -n '1d;1,2p'

whose behavior differ in traditional (heirloom toolchest at
least) and GNU.

It's unclear to me whether POSIX wants one behavior or the
other.

因此,(根据Geoff的观点),POSIX很明显表明GNU行为是不合规的。

的确,即使对于那些不知道如何处理范围的人(甚至Geoff最初都发现符合性的行为“奇怪”),它的一致性也不如(seq 10 | sed -n '1d;1,2p'seq 10 | sed -n '1d;/^1$/,2p')一致。

没有人愿意将其作为错误报告给GNU员工。我不确定我是否会将其视为错误。最好的选择可能是对POSIX规范进行更新,以使两种行为都可以清楚地表明一个人不能依赖任何一个。

编辑。现在,我看了sed70年代末期Unix V7 中的原始实现,它看起来很像数字地址的行为不是故意的,或者至少根本没有考虑过。

相反,在杰夫(Geoff)对规范的阅读(以及我对它发生的原因的原始解释)之后,他得出以下结论:

seq 5 | sed -n '3d;1,3p'

第1、2、4和5行应该被输出,因为这一次,它是1,3p远程命令从未遇到的结束地址,例如seq 5 | sed -n '3d;/1/,/3/p'

但是,这在原始实现中以及我尝试过的任何其他实现中都没有发生(busybox sed返回第1、2和4行,看上去更像是一个错误)。

如果查看UNIX v7代码,它将检查当前行号是否大于(数字)结束地址,然后超出范围。它没有针对起始地址执行此操作的事实似乎比有意设计更像是疏忽。

这意味着在此方面,目前尚没有任何实际符合POSIX规范解释的实现。

GNU实现的另一个令人困惑的行为是:

$ seq 5 | sed -n '2d;2,/3/p'
3
4
5

由于跳过了第2行,因此2,/3/在第3行(数字> = 2的第一行)中输入。但是因为是使我们进入范围的那一行,所以没有检查结束地址。随着busybox sedin 变得更糟:

$ seq 10 | busybox sed -n '2,7d; 2,3p'
8

由于第2至7行已删除,因此第8行是第一个> = 2的行,因此输入了2,3范围!


1
因此,听起来该问题仍未解决-我同意您为什么会这样,但也不清楚是否要这样做-尽管听起来确实像Geoff所引用的文字深信UNIX™实现是正确的。那也是你的读物吗?
迈克尔·荷马

1
@MichaelHomer,这个想法是(根据Geoff的观点)POSIX 清楚地表明GNU行为是不合规的。而且这是真的它不太一致(比较seq 10 | sed -n '1d;1,2p'seq 10 | sed -n '1d;/^1$/,2p'),即使可能没那么令人惊讶的人就没有意识到范围如何处理。没有人愿意将其报告为GNU人员的错误。我不确定我是否将其视为错误,可能最好的选择是更新POSIX规范,以允许两种行为明确表明它们不能依赖任何一种。
斯特凡Chazelas

2
实际上,由于POSIX定义没有声明必须“看到”地址才能开始或结束地址范围,因此IMO GNU实现会更严格地遵循POSIX的用语(对于GNU而言令人惊讶!)。对于我所知道的大多数实际情况,这也是理想的行为。但是,正如您指出的那样,它需要保持一致。并且甚至在每行之后d都检查范围模式不仅是性能问题,还会导致进一步的实现问题,因为范围所需的“看不见的”模式不允许对其他空模式产生影响……一团糟!
Philippos

@Philippos,在该1d;1,2p脚本中,1,2p命令不在第一行上运行,因此第一个地址没有任何模式空间匹配,这是解释该文本的一种方式。在任何情况下,显然应该在运行命令时对地址进行评估。像sed 's/./x/g; /xxx/,/xxx/d'
斯特凡Chazelas

2
@Isaac,这是问题的核心。在POSIX语言中1/1/都是地址,1是行号为1 /1/时的地址,是模式空间包含时的地址1,问题是两种地址类型应被视为相同,还是应将行号范围视为“绝对”,无论它们是否匹配。有关更多历史背景,另请参阅我的最新编辑。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.