在sed中使用多个感叹号有什么意义?


12

POSIX sed文档说:

一个功能可以前面有一个或多个'!' 字符,在这种情况下,如果地址未选择模式空间,则应应用该功能。零个或多个<空白>字符应在第一个'!'之前接受 字符。尚不确定<blank>字符是否可以跟在'!'之后 字符,符合标准的应用程序不得使用“!” 带有<blank>字符的字符。

因此,使用任何POSIX sed,我们都可以:

sed -e '/pattern/!d' file

与写作相同:

sed -e '/pattern/!!d' file

!!!dn感叹号的标志仍然是罚款(含三级测试sed的版本传家宝工具箱中)。我看不到多个感叹号之间有什么好处。

为什么规范中允许使用该语法?在实际应用中它如何有用?


在这种情况下,GNU sed似乎不兼容,如果我们使用多个感叹号,它将抱怨:

$ sed -e '/pattern/!!d' file
sed: -e expression #1, char 11: multiple `!'s

2
FWIW:在OpenBSD上,!它用作切换按钮,/pattern/!!与相同/pattern/,并且/pattern/!!!与相同/pattern/!。在FreeBSD上,多个!与单个相同。
lcd047

2
规范中很多内容的要点是sed可以生成脚本。给定POSIX sed,对脚本的编写进行脚本编写应该是一个非常现实的简单事情sed。因此,如果您在某种情况下有一些触发条件,应该标记出一个!不值得您采取任何行动的地址,那么您甚至可能针对同一事件多次触发该地址,但仍会得出相同的结果。
mikeserv

@cuonglm不,只有FreeBSD是。GNU,OpenBSD和NetBSD sed则不是。
lcd047

@ lcd047:是的,当然。对不起,我的英语不好。我的意思是,它不符合要求。很高兴知道这一点。但是我的问题的重点是,使用POSIX sed,该语法如何在现实世界中有用?
cuonglm

1
FWIW:对此的修复已在OpenBSD-current中提交。
lcd047

Answers:


5

sed的API是原始的-这是设计使然。至少,它在设计上仍然是原始的-我不能说它是否一开始就是原始设计的。在大多数情况下,编写一个sed脚本,该脚本在运行时将输出另一个sed脚本,这确实很简单。sed通常由宏预处理器(例如m4和/或)以这种方式应用make

(下面是一个高度假设的用例:这是一个设计成适合解决方案的问题。如果您觉得这很麻烦,那可能是因为确实如此,但这并不一定会使它的有效性降低。)


考虑以下输入文件:

cat <<"" >./infile
camel
cat dog camel
dog cat
switch
upper
lower

如果我们想编写一个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|'

...以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

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

camel-case
upper-case
lower-case

现在您可能会问我...我为什么要这样做?为什么我不只是锚定grep比赛?谁使用骆驼套?我只能回答每个问题,我不知道...因为我不知道。在阅读此问题之前,我从未亲自注意到多!规范中的解析要求-我认为这很不错。

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

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

我认为那多!在这种情况下,地址可能比在其他情况下更有用,但是老实说,我想不出一个可以很好地利用它的情况-我sed很多。我也认为值得一提的是,GNU / BSD sed都无法按指定的方式处理它-这可能不是规范中要求很高的一个方面,因此,如果实现忽略了它,我非常怀疑他们的bugs @ box是否会遭受损失结果非常可怕。

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


1
现在已在OpenBSD-current中修复。
lcd047

1
下一个规范!中将删除多个,这里发生了什么!
cuonglm

@cuonglm-我想太少了太晚了。也许我比我想的要近。
mikeserv

@cuonglm-好吧,那是什么... 被标记为“ Marked”的意思是?
mikeserv

1
@mikeserv:答案解释了我的奇迹,并给了我sed API的另一种观点。这对我来说很有意义!
cuonglm
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.