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)
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
脚本。您可能会发现所需的\n
ewline 分隔符[wr:bt{]
在这种情况下更有意义,如果您牢记这一想法,您可能会更好地理解规范的其他一些方面 - (例如不:
接受地址,并q
拒绝接受任何超过 1)。
在上面的例子中我写了某种形式的sed
脚本,只能永远被读取一次。如果您仔细观察它,您可能会注意到,在sed
读取编辑文件时,它会从一个命令块前进到下一个命令块 - 它永远不会脱离或完成其编辑脚本,直到它完全通过其编辑文件。
我认为多!地址在这种情况下可能比在其他情况下更有用,但是,老实说,我想不出一个案例可以很好地利用它 - 而我sed
很多。我还认为值得注意的是 GNU/BSDsed
都未能按规定处理它 - 这可能不是规范中需求量很大的一个方面,所以如果一个实现忽略了它,我非常怀疑他们的bugs@ box 会受到影响结果非常糟糕。
也就是说,未能按规定处理此问题是任何假装合规的实现的错误,因此我认为此处需要向相关的开发人员发送电子邮件,如果您不这样做,我打算这样做。