sed中的“保留空间”和“模式空间”的概念


86

我对sed中的两个概念感到困惑:保持空间和模式空间。有人可以帮忙解释一下吗?

这是手册的摘要:

h H    Copy/append pattern space to hold space.
g G    Copy/append hold space to pattern space.

n N    Read/append the next line of input into the pattern space.

这六个命令确实使我感到困惑。


4
自己尝试:echo $'1\n2\n3\n4' | sed -n '1~2h;2~2{p;x;p}'
choroba

4
不要感到困惑,只是不要使用它们。除了单行上的简单替换以外,您应该使用awk而不是sed。在没有更好的选择之前,保持空间,模式空间和95%的sed语言构造是在awk之前发明的。当awk在1970年代中期被发明时,它们就过时了,直到今天,那些喜欢使用seds arcane语法解决问题而不是简单而刻板地在awk中解决问题的人才使它们存活下来。如果您在sed中使用s,g和p(带有-n),则肯定可以使用错误的工具。
Ed Morton

26
Morton awk处理结构化数据(每行具有相同的结构)。Sed用于处理原始随机数据。因此,您不能只是简单地使用awk而不是sed。
Pithikos 2014年

5
我强烈建议阅读info sed。它比裸手页面要详细得多。
费尔南多·巴索

4
我同意皮蒂科斯的观点。我像莫顿一样走下车道,问自己和莫顿一样的问题。但是,我还不能轻易驳回sed。
eigenfield'3

Answers:


111

sed逐行读取文件时,当前已读取的行将插入到模式缓冲区(模式空间)中。模式缓冲区类似于临时缓冲区,即存储当前信息的暂存器。当您告诉sed打印时,它将打印图案缓冲区。

保持缓冲区/保持空间就像一个长期存储,因此您可以捕获某些东西,将其存储起来,然后在sed处理另一行时再使用它。您不直接处理保留空间,相反,如果要对其进行处理,则需要将其复制或追加到模式空间。例如,print命令p仅打印图案空间。同样,s在模式空间上操作。

这是一个例子:

sed -n '1!G;h;$p'

(-n选项禁止自动打印行)

这里有三个命令:1!Gh$p1!G有一个地址,1(第一行),但!该命令将被执行到处手段,但在第一行上。$p另一方面,只会在最后一行执行。那么会发生什么:

  1. 读取第一行并将其自动插入模式空间
  2. 在第一行,不执行第一条命令;h将第一行复制到保留空间。
  3. 现在第二行替换了模式空间中的所有内容
  4. 在第二行中,首先执行G,将保留缓冲区的内容追加到模式缓冲区,并用换行符将其分隔。模式空间现在包含第二行,换行符和第一行。
  5. 然后,h命令将模式缓冲区的并置内容插入到保留空间中,该空间现在保留两行和一行的反向行。
  6. 我们继续进行第三行-转到上面的点(3)。

最后,在读取最后一行并将保持空间(以相反的顺序包含所有先前的行)添加到图案空间之后,用来打印图案空间p。您已经猜到了,上面的tac命令确实执行了该命令-反向打印文件。


3
G和h选项是否像“剪切并附加”一样工作?它看起来不像“复制并追加”操作。
微笑

使用嵌套命令(大括号)时,会在模式和保留空间后面添加什么?'195,210{/add/p}'…是否可以提取模式中涉及的一组线的最后一行?
桑德堡

17

@Ed Morton:我在这里不同意你的看法。我发现sed非常有用且简单(一旦您了解了模式的概念并保持缓冲区),就可以提出一种优雅的方式来进行多行grepping。

例如,让我们以一个文本文件为例,该文件包含主机名和有关每个主机的一些信息,中间有很多我不关心的垃圾。

Host: foo1
some junk, doesnt matter
some junk, doesnt matter
Info: about foo1 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
Info: a second line about foo1 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter
Host: foo2
some junk, doesnt matter
Info: about foo2 that I really care about!!
some junk, doesnt matter
some junk, doesnt matter

对我来说,一个awk脚本仅获取带有主机名的行和相应的info行将比我用sed所能做的要多一些:

sed -n '/Host:/{h}; /Info/{x;p;x;p;}' myfile.txt

输出看起来像:

Host: foo1
Info: about foo1 that I really care about!!
Host: foo1
Info: a second line about foo1 that I really care about!!
Host: foo2
Info: about foo2 that I really care about!!

(请注意,Host: foo1在输出中会出现两次。)

说明:

  1. -n 除非明确打印,否则禁用输出
  2. 第一次匹配,找到并将Host:行放入保持缓冲区(h)
  3. 第二个匹配项,找到下一个Info:行,但首先在模式缓冲区中与保持缓冲区交换(x)当前行,并打印(p)该Host:行,然后重新交换(x)并打印(p)Info:行。

是的,这是一个简单的例子,但是我怀疑这是一个常见的问题,它很快就由简单的sed单线解决。对于更复杂的任务,例如您不能依赖给定的可预测序列的任务,awk可能更适合。


2
在这种情况下,您可以使用grep:grep 'Host\|Info'
Pithikos 2014年

如果给定主机后有两个信息行,则@JensJenson希望两个信息行都以一个信息行开头。我想我将相应地编辑答案。Pithikos,那么grep就不够了。
2014年

3
@JensJenson,awk相当于您的sed代码也很短:awk '/Host:/{hold=$0}; /Info/{print hold; print;}' myfile.txt
Aaron McDaid 2014年

11

尽管@January的答案和示例很好,但对我来说解释还不够。在设法了解其sed -n '1!G;h;$p'工作原理之前,我不得不进行大量学习和学习。所以我想为像我这样的人详细说明命令。

首先,让我们看看命令的作用。

$ echo {a..d} | tr ' ' '\n' # Prints from 'a' to 'd' in each line
a
b
c
d
$ echo {a..d} | tr ' ' '\n' | sed -n '1!G;h;$p'
d
c
b
a

tac命令一样反转输入。

sed逐行读取,所以让我们看看在patten空间和每一行的保持空间上会发生什么。当h命令将模式空间的内容复制到保持空间时,两个空间的文本相同。

Read line    Pattern Space / Hold Space    Command executed
-----------------------------------------------------------
a            a$                            h
b            b\na$                         1!G;h
c            c\nb\na$                      1!G;h
d            d\nc\nb\na$                   1!G;h;$p

在最后一行,$p打印d\nc\nb\na$格式为

d
c
b
a

如果要查看每行的模式空间,可以添加l命令。

$ echo {a..d} | tr ' ' '\n' | sed -n '1!G;h;l;$p'
a$
b\na$
c\nb\na$
d\nc\nb\na$
d
c
b
a

我发现观看此视频教程对了解sed的工作原理非常有帮助,因为该家伙演示了如何逐步使用每个空间。在第4篇教程中会提到保持间隔,但是如果您不熟悉,我建议您观看所有视频sed

此外GNU sed的文档布鲁斯·巴内特的桑达教程是非常好的参考。


2
我认为提及所有实际用途的容纳空间为空是有益的,除非我们添加一些内容。
Naveed
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.