What is the point of using multiple exclamation marks in sed?

cuo*_*glm 13 sed posix

POSIX sed documentation said:

A function can be preceded by one or more '!' characters, in which case the function shall be applied if the addresses do not select the pattern space. Zero or more <blank> characters shall be accepted before the first '!' character. It is unspecified whether <blank> characters can follow a '!' character, and conforming applications shall not follow a '!' character with <blank> characters.

So, with any POSIX sed, we can:

sed -e '/pattern/!d' file
Run Code Online (Sandbox Code Playgroud)

It's the same as writing:

sed -e '/pattern/!!d' file
Run Code Online (Sandbox Code Playgroud)

And !!!d and n of exclamation marks are still be fine (Tested with three sed version from heirloom toolchest). I don't see any benefit between multiple instead of one exclamation.

Why did the spec allow that syntax and how is it useful in real world application?


It seems that GNU sed is not compliant in this case, it will complain if we use multiple exclamations:

$ sed -e '/pattern/!!d' file
sed: -e expression #1, char 11: multiple `!'s
Run Code Online (Sandbox Code Playgroud)

mik*_*erv 6

sed的 API 是原始的 - 这是设计使然。至少,它在设计上一直保持原始——我不能说它是否是在开始时设计的原始。在大多数情况下,编写一个sed在运行时会输出另一个sed脚本的脚本确实是一件简单的事情。sed宏预处理器经常以这种方式应用m4和/或make

(接下来是一个高度假设的用例:这是一个为适应解决方案而设计的问题。如果你觉得这有点牵强,那可能是因为它确实如此,但这并不一定会降低它的有效性。)


考虑以下输入文件:

cat <<"" >./infile
camel
cat dog camel
dog cat
switch
upper
lower
Run Code Online (Sandbox Code Playgroud)

如果我们想编写一个sed脚本,将单词-case附加到上述输入文件中每个合适单词的尾部,前提是它可以在合适的上下文中的一行中找到,并且我们希望尽可能高效地这样做(作为我们的目标,例如,在编译操作期间)那么我们应该尽可能避免应用/regexp /

我们可能会做的一件事是立即在我们的系统上预编辑文件,并且sed在编译期间根本不要调用。但是,如果根据本地设置和/或编译时选项应该或不应该包含文件中的任何这些词,那么这样做可能不是一个理想的选择。

我们可能要做的另一件事是现在针对正则表达式处理文件。我们可以生成 - 并在我们的编译中包含 - 一个sed可以根据行号应用编辑的脚本 - 从长远来看,这通常是一种更有效的途径。

例如:

n=$(printf '\\\n\t')
grep -En 'camel|upper|lower' <infile |
sed "   1i${n%?}#!/usr/heirloom/bin/posix2001/sed -nf
        s/[^:]*/:&$n&!n;&!b&$n&/;s/://2;\$a${n%?}q"'
        s/ *cat/!/g;s/ *dog/!/g
        s| *\([cul][^ ]*\).*|s/.*/\1-case/p|'
Run Code Online (Sandbox Code Playgroud)

...以sed脚本的形式写入输出,看起来像...

#!/usr/heirloom/bin/posix2001/sed -nf
:1
    1!n;1!b1
    1s/.*/camel-case/p
:2
    2!n;2!b2
    2!!s/.*/camel-case/p
:5
    5!n;5!b5
    5s/.*/upper-case/p
:6
    6!n;6!b6
    6s/.*/lower-case/p
q
Run Code Online (Sandbox Code Playgroud)

当该输出保存到我​​的机器上名为./bang.sed并运行的可执行文本文件时./bang.sed ./infile,输出为:

camel-case
upper-case
lower-case
Run Code Online (Sandbox Code Playgroud)

现在你可能会问我......我为什么要这样做?为什么我不只是锚grep的比赛?谁会用驼峰壳?对于每个我只能回答的问题,我不知道……因为我不知道。在阅读这个问题之前,我从来没有亲自注意到多!规范中的解析要求 - 我认为这是一个非常巧妙的问题。

多!不过,这对我来说确实很有意义 - 大部分sed规范都针对简单解析和简单生成的 sed脚本。您可能会发现所需的\newline 分隔符[wr:bt{]在这种情况下更有意义,如果您牢记这一想法,您可能会更好地理解规范的其他一些方面 - (例如不:接受地址,并q拒绝接受任何超过 1)

在上面的例子中我写了某种形式的sed脚本,只能永远被读取一次。如果您仔细观察它,您可能会注意到,在sed读取编辑文件时,它会从一个命令块前进到下一个命令块 - 它永远不会脱离或完成其编辑脚本,直到它完全通过其编辑文件。

我认为多!地址在这种情况下可能比在其他情况下更有用,但是,老实说,我想不出一个案例可以很好地利用它 - 而我sed很多。我还认为值得注意的是 GNU/BSDsed都未能按规定处理它 - 这可能不是规范中需求量很大的一个方面,所以如果一个实现忽略了它,我非常怀疑他们的bugs@ box 会受到影响结果非常糟糕。

也就是说,未能按规定处理此问题任何假装合规的实现的错误,因此我认为此处需要向相关的开发人员发送电子邮件,如果您不这样做,我打算这样做。