Stream 编辑器 - 管理模式

我们已经讨论了模式和保持缓冲区的使用。 在本章中,我们将更多地探讨它们的用法。 让我们讨论打印模式空间的 n 命令。 它将与其他命令一起使用。 下面给出了 then 命令的语法。

[address1[,address2]]n

让我们举个例子。

[jerry]$ sed 'n' books.txt 

执行上述代码时,会产生如下结果:

1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352 
3) The Alchemist, Paulo Coelho, 197 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
5) The Pilgrimage, Paulo Coelho, 288 
6) A Game of Thrones, George R. R. Martin, 864 

n 命令打印模式缓冲区的内容,清除模式缓冲区,获取下一行到模式缓冲区,然后对其应用命令。

让我们考虑在 n 之前有三个 SED 命令,在 n 之后有两个 SED 命令如下:

Sed command #1 
Sed command #2 
Sed command #3 
n command 
Sed command #4 
Sed command #5

在这种情况下,SED 将前三个命令应用于模式缓冲区,清除模式缓冲区,将下一行提取到模式缓冲区,然后对其应用第四个和第五个命令。 这是一个非常重要的概念。 不要在没有清楚了解这一点的情况下继续前进。

保持缓冲区保存数据,但 SED 命令不能直接应用于保持缓冲区。 因此,我们需要将保持缓冲区数据带入模式缓冲区。 SED 提供 x 命令来交换模式和保持缓冲区的内容。 以下命令说明了 x 命令。

让我们稍微修改一下books.txt 文件。 比如说,该文件包含书名,后跟作者姓名。 修改后的文件应该是这样的:

[jerry]$ cat books.txt

执行上述代码,得到如下结果:

A Storm of Swords 
George R. R. Martin 
The Two Towers 
J. R. R. Tolkien 
The Alchemist 
Paulo Coelho 
The Fellowship of the Ring 
J. R. R. Tolkien 
The Pilgrimage 
Paulo Coelho 
A Game of Thrones 
George R. R. Martin 

让我们交换两个缓冲区的内容。 例如,以下示例仅打印作者的姓名。

[jerry]$ sed -n 'x;n;p' books.txt 

执行上述代码,得到如下结果:

George R. R. Martin 
J. R. R. Tolkien 
Paulo Coelho 
J. R. R. Tolkien 
Paulo Coelho 
George R. R. Martin 

让我们了解一下这个命令是如何工作的。

  • 最初,SED 将第一行,即 A Storm of Swords 读入模式缓冲区。

  • x 命令将此行移动到保持缓冲区。

  • n 将下一行,即 George R. R. Martin 提取到模式缓冲区中。

  • 控制权传递给后跟 n 的命令,该命令打印模式缓冲区的内容。

  • 这个过程会一直重复,直到文件用完。

现在让我们在打印之前交换缓冲区的内容。 猜猜,会发生什么? 是的,它打印书名。

[jerry]$ sed -n 'x;n;x;p' books.txt 

执行上述代码,得到如下结果:

A Storm of Swords 
The Two Towers 
The Alchemist 
The Fellowship of the Ring 
The Pilgrimage 
A Game of Thrones

h 命令处理保持缓冲区。 它将数据从模式缓冲区复制到保持缓冲区。 保持缓冲区中的现有数据被覆盖。请注意,h 命令不会移动数据,它只会复制数据。 因此,复制的数据在模式缓冲区中保持原样。 下面给出了 h 命令的语法。

[address1[,address2]]h 

以下命令仅打印作者 Paulo Coelho 的标题。

[jerry]$ sed -n '/Paulo/!h; /Paulo/{x;p}' books.txt 

执行上述代码,得到如下结果:

The Alchemist 
The Pilgrimage

让我们了解上述命令的工作原理。 books.txt 的内容遵循特定的格式。 第一行是书名,后面是书的作者。 在上面的命令中,"!" 用于反转条件,即仅当模式匹配不成功时才将行复制到保持缓冲区。 并且花括号 {} 用于对多个 SED 命令进行分组

在命令的第一遍中,SED 将第一行,即 A Storm of Swords 读入模式缓冲区并检查它是否包含模式 Paulo。 由于模式匹配不成功,它将这一行复制到保持缓冲区。 现在模式缓冲区和保持缓冲区都包含同一行,即 A Storm of Swords。 在第二步中,它检查该行是否包含 Paulo 模式。 由于模式不匹配,它不做任何事情。

在第二遍中,它将下一行 George R. R. Martin 读入模式缓冲区并应用相同的步骤。 对于接下来的三行,它做同样的事情。 在第五遍结束时,两个缓冲区都包含 The Alchemist。 在第六遍开始时,它读取 Paulo Coelho 行,当模式匹配时,它不会将此行复制到保持缓冲区中。 因此,模式缓冲区包含 Paulo Coelho,而保持缓冲区包含 The Alchemist。

此后,它检查模式缓冲区是否包含模式 Paulo。 随着模式匹配成功,它将模式缓冲区的内容与保持缓冲区交换。 现在模式缓冲区包含炼金术士,保持缓冲区包含 Paulo Coelho。 最后,它打印模式缓冲区的内容。 相同的步骤适用于朝圣图案。

h 命令破坏保持缓冲区的先前内容。 这并不总是可以接受的,因为有时我们需要保留内容。为此,SED 提供了 H 命令,该命令通过在末尾添加新行将内容附加到保持缓冲区。hH 命令之间的唯一区别是,前者覆盖了保持缓冲区中的数据,而后者将数据附加到保持缓冲区。 它的语法类似于 h 命令。

[address1[,address2]]H

让我们再举一个例子。 这一次,不仅打印书名,还打印作者的姓名。 以下示例打印书名及其作者姓名。

[jerry]$ sed -n '/Paulo/!h; /Paulo/{H;x;p}' books.txt 

执行上述代码,得到如下结果:

The Alchemist 
Paulo Coelho 
The Pilgrimage
Paulo Coelho

我们学习了如何复制/附加模式缓冲区的内容来保存缓冲区。 我们也可以执行反向功能吗? 是的,当然了! 为此,SED 提供了 g 命令,该命令将数据从保持缓冲区复制到模式缓冲区。复制时,模式空间中的现有数据会被覆盖。 下面给出了 g 命令的语法。

[address1[,address2]]g

让我们考虑同样的例子——印刷书名及其作者。 这一次,我们将首先打印作者的姓名,然后在下一行打印相应的书名。 以下命令打印作者 Paulo Coelho 的姓名,后跟书名。

[jerry]$ sed -n '/Paulo/!h; /Paulo/{p;g;p}' books.txt 

执行上述代码,得到如下结果:

Paulo Coelho 
The Alchemist 
Paulo Coelho 
The Pilgrimage

第一个命令保持原样。 在第五遍结束时,两个缓冲区都包含 The Alchemist。 在第六遍开始时,它读取 Paulo Coelho 行,当模式匹配时,它不会将此行复制到保持缓冲区中。 因此,模式空间包含 Paulo Coelho,而保持空间包含 The Alchemist。

此后,它检查模式空间是否包含模式 Paulo。 当模式匹配成功时,它首先打印模式空间的内容,即 Paulo Coelho,然后将保持缓冲区复制到模式缓冲区。 因此,模式和保持缓冲区都包含炼金术士。 最后,它打印模式缓冲区的内容。 相同的步骤适用于朝圣图案。

同样,我们可以将保持缓冲区的内容附加到模式缓冲区。 SED 提供了 G 命令,该命令通过在末尾添加新行将内容附加到模式缓冲区。

[address1[,address2]]G

现在让我们以前面的例子为例,它打印出作者 Paulo Coelho 的名字,然后是书名。 要获得相同的结果,请执行以下 SED 命令。

[jerry]$ sed -n '/Paulo/!h; /Paulo/{G;p}' books.txt

执行上述代码,得到如下结果:

Paulo Coelho 
The Alchemist 
Paulo Coelho 
The Pilgrimage

你能修改上面的例子来显示作者后面的书名吗? 很简单,只需在 G 命令之前交换缓冲区内容。

[jerry]$ sed -n '/Paulo/!h; /Paulo/{x;G;p}' books.txt

执行上述代码,得到如下结果:

The Alchemist 
Paulo Coelho 
The Pilgrimage 
Paulo Coelho