流编辑器 - 快速指南




流编辑器 - 概述

缩写 SED 代表 **Stream EDitor**。它是一个简单但强大的实用程序,可以解析文本并无缝地转换它。SED 由贝尔实验室的 Lee E. McMahon 于 1973-74 年开发。如今,它运行在所有主要的操作系统上。

McMahon 编写了一个通用的面向行的编辑器,最终成为了 SED。SED 借鉴了 ed 编辑器的语法和许多有用的功能。从一开始,它就支持正则表达式。SED 接受来自文件以及管道的输入。此外,它还可以接受来自标准输入流的输入。

SED 由自由软件基金会 (FSF) 编写和维护,并由 GNU/Linux 发布。因此,它通常被称为 **GNU SED**。对于新手用户来说,SED 的语法可能看起来很神秘。但是,一旦你熟悉了它的语法,你就可以用几行 SED 脚本解决许多复杂的任务。这就是 SED 的魅力所在。

SED 的典型用途

SED 可以以多种不同的方式使用,例如

  • 文本替换,
  • 选择性打印文本文件,
  • 就地编辑文本文件,
  • 非交互式编辑文本文件,等等。

流编辑器 - 环境

本章介绍如何在你的 GNU/Linux 系统上设置 SED 环境。

使用包管理器安装

通常,SED 在大多数 GNU/Linux 发行版中默认可用。使用 **which** 命令来确定它是否在你的系统上存在。如果不存在,则使用如下所示的 **apt** 包管理器在基于 Debian 的 GNU/Linux 上安装 SED

[jerry]$ sudo apt-get install sed 

安装后,确保可以通过命令行访问 SED。

[jerry]$ sed --versio

执行上述代码后,你将得到以下结果

sed (GNU sed) 4.2.2 
Copyright (C) 2012 Free Software Foundation, Inc. 
License GPLv3+: GNU GPL version 3 or later . 
This is free software: you are free to change and redistribute it. 
There is NO WARRANTY, to the extent permitted by law.  
Written by Jay Fenlason, Tom Lord, Ken Pizzini, 
and Paolo Bonzini. 
GNU sed home page: . 
General help using GNU software: . 
E-mail bug reports to: . 
Be sure to include the word "sed" somewhere in the "Subject:" field.

类似地,要在基于 RPM 的 GNU/Linux 上安装 SED,请使用如下所示的 yum 包管理器

[root]# yum -y install sed

安装后,确保可以通过命令行访问 SED。

[root]# sed --version

执行上述代码后,你将得到以下结果

GNU sed version 4.2.1 
Copyright (C) 2009 Free Software Foundation, Inc. 
This is free software; see the source for copying conditions.  There is NO 
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, 
to the extent permitted by law.  
GNU sed home page: . 
General help using GNU software: . 
E-mail bug reports to: . 
Be sure to include the word "sed" somewhere in the "Subject:" field.

从源代码安装

由于 GNU SED 是 GNU 项目的一部分,因此其源代码可免费下载。我们已经了解了如何使用包管理器安装 SED。现在让我们了解如何从其源代码安装 SED。

以下安装适用于任何 GNU/Linux 软件,以及大多数其他免费提供的程序。以下是安装步骤

  • 从可靠的地方下载源代码。命令行实用程序 **wget** 用于此目的。

  • [jerry]$ wget ftp://ftp.gnu.org/gnu/sed/sed-4.2.2.tar.bz2
    
  • 解压缩并提取下载的源代码。

  • [jerry]$ tar xvf sed-4.2.2.tar.bz2 
    
  • 切换到目录并运行 configure。

  • [jerry]$ ./configure 
    
  • 成功完成后,**configure** 将生成 Makefile。要编译源代码,请发出 **make** 命令。

  • [jerry]$ make
    
  • 你可以运行测试套件以确保构建是干净的。这是一个可选步骤。

  • [jerry]$ make check 
    
  • 最后,安装 SED 实用程序。确保你拥有超级用户权限。

  • [jerry]$ sudo make install 
    

就是这样!你已成功编译并安装了 SED。通过如下所示执行 **sed** 命令来验证它

[jerry]$ sed --version

执行上述代码后,你将得到以下结果

sed (GNU sed) 4.2.2 
Copyright (C) 2012 Free Software Foundation, Inc. 
License GPLv3+: GNU GPL version 3 or later . 
This is free software: you are free to change and redistribute it. 
There is NO WARRANTY, to the extent permitted by law.  
Written by Jay Fenlason, Tom Lord, Ken Pizzini, 
and Paolo Bonzini. 
GNU sed home page: . 
General help using GNU software: . 
E-mail bug reports to: . 
Be sure to include the word "sed" somewhere in the "Subject:" field.

流编辑器 - 工作流程

在本章中,我们将探讨 SED 的确切工作原理。要成为一名专家级 SED 用户,需要了解其内部机制。SED 遵循一个简单的工作流程:读取、执行和显示。下图描述了工作流程。

Stream Editor Workflow
  • **读取**:SED 从输入流(文件、管道或 stdin)读取一行并将其存储在其称为 **模式缓冲区** 的内部缓冲区中。

  • **执行**:所有 SED 命令都按顺序应用于模式缓冲区。默认情况下,SED 命令应用于所有行(全局),除非指定行地址。

  • **显示**:将(修改后的)内容发送到输出流。发送数据后,模式缓冲区将为空。

  • 上述过程重复,直到文件耗尽。

注意事项

  • 模式缓冲区是 SED 使用的私有、内存中、易失性存储区域。

  • 默认情况下,所有 SED 命令都应用于模式缓冲区,因此输入文件保持不变。GNU SED 提供了一种就地修改输入文件的方法。我们将在后面的章节中探讨它。

  • 还有一个名为 **保持缓冲区** 的内存区域,它也是私有的、内存中的、易失性存储区域。数据可以存储在保持缓冲区中以供以后检索。在每个循环结束时,SED 会删除模式缓冲区的内容,但保持缓冲区的内容在 SED 循环之间保持持久。但是 SED 命令不能直接在保持缓冲区上执行,因此 SED 允许在保持缓冲区和模式缓冲区之间移动数据。

  • 最初,模式缓冲区和保持缓冲区都为空。

  • 如果没有提供输入文件,则 SED 接受来自标准输入流 (stdin) 的输入。

  • 如果未提供地址范围,则默认情况下 SED 对每一行进行操作。

示例

让我们创建一个名为 **quote.txt** 的文本文件,其中包含著名作家保罗·科埃略的一段引言。

[jerry]$ vi quote.txt 
There is only one thing that makes a dream impossible to achieve: the fear of failure. 
 - Paulo Coelho, The Alchemist

为了理解 SED 的工作流程,让我们使用 SED 显示文件 quote.txt 的内容。此示例模拟了 **cat** 命令。

[jerry]$ sed '' quote.txt

执行上述代码时,将产生以下结果。

There is only one thing that makes a dream impossible to achieve: the fear of failure. 

在上面的示例中,quote.txt 是输入文件名,在其前面有一对单引号,表示 SED 命令。让我们来揭开这个操作的神秘面纱。

首先,SED 从输入文件 quote.txt 读取一行并将其存储在其模式缓冲区中。然后它在模式缓冲区上应用 SED 命令。在我们的例子中,没有 SED 命令,因此没有对模式缓冲区执行任何操作。最后,它删除并打印模式缓冲区的内容到标准输出。是不是很简单?

在以下示例中,SED 接受来自标准输入流的输入。

[jerry]$ sed '' 

执行上述代码时,将产生以下结果。

There is only one thing that makes a dream impossible to achieve: the fear of failure. 
There is only one thing that makes a dream impossible to achieve: the fear of failure.

这里,第一行通过键盘输入,第二行是 SED 生成的输出。要退出 SED 会话,请按 ctrl-D (^D)。

流编辑器 - 基本语法

本章介绍了 SED 支持的基本命令及其命令行语法。SED 可以以下两种形式调用

sed [-n] [-e] 'command(s)' files 
sed [-n] -f scriptfile files

第一种形式允许在命令行中指定命令,并将它们括在单引号中。后者允许指定包含 SED 命令的脚本文件。但是,我们可以多次将这两种形式结合使用。SED 提供各种命令行选项来控制其行为。

让我们看看如何指定多个 SED 命令。SED 提供 **delete** 命令来删除某些行。让我们删除第 1、2 和第 5 行。目前,忽略 delete 命令的所有细节。我们将在后面讨论更多关于 delete 命令的内容。

首先,使用 **cat** 命令显示文件内容。

[jerry]$ cat 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

现在指示 SED 只删除某些行。这里,为了删除三行,我们使用 -e 选项指定了三个单独的命令。

[jerry]$ sed -e '1d' -e '2d' -e '5d' books.txt 

执行上述代码后,你将得到以下结果

3) The Alchemist, Paulo Coelho, 197 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
6) A Game of Thrones, George R. R. Martin, 864 

此外,我们可以在一个文本文件中编写多个 SED 命令,并将该文本文件作为参数提供给 SED。SED 可以对模式缓冲区应用每个命令。以下示例说明了 SED 的第二种形式。

首先,创建一个包含 SED 命令的文本文件。为了便于理解,让我们使用相同的 SED 命令。

[jerry]$ echo -e "1d\n2d\n5d" > commands.txt 
[jerry]$ cat commands.txt

执行上述代码后,你将得到以下结果

1d 
2d 
5d 

现在指示 SED 从文本文件读取命令。这里,我们实现了与上面示例中相同的输出。

[jerry]$ sed -f commands.txt books.txt

执行上述代码后,你将得到以下结果

3) The Alchemist, Paulo Coelho, 197 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
6) A Game of Thrones,George R. R. Martin, 864 

标准选项

SED 支持以下标准选项

  • -n: 默认打印模式缓冲区。例如,以下 SED 命令不显示任何输出

  • [jerry]$ sed -n '' quote.txt 
    
  • -e: 下一个参数是一个编辑命令。这里,尖括号表示必填参数。通过使用此选项,我们可以指定多个命令。让我们将每一行打印两次

  • [jerry]$ sed -e '' -e 'p' quote.txt
    

执行上述代码后,你将得到以下结果

There is only one thing that makes a dream impossible to achieve: the fear of failure. 
There is only one thing that makes a dream impossible to achieve: the fear of failure. 
 - Paulo Coelho, The Alchemist 
 - Paulo Coelho, The Alchemist
  • -f: 下一个参数是一个包含编辑命令的文件。尖括号表示必填参数。在以下示例中,我们通过文件指定 print 命令

[jerry]$ echo "p" > commands 
[jerry]$ sed -n -f commands quote.txt

执行上述代码后,你将得到以下结果

There is only one thing that makes a dream impossible to achieve: the fear of failure. 
 - Paulo Coelho, The Alchemist

GNU 特定选项

让我们快速浏览一下 GNU 特定的 SED 选项。请注意,这些选项是 GNU 特定的;可能不受其他 SED 变体支持。在后面的章节中,我们将更详细地讨论这些选项。

  • -n, --quiet, --silent: 与标准 -n 选项相同。

  • -e script, --expression=script: 与标准 -e 选项相同。

  • -f script-file, --file=script-file: 与标准 -f 选项相同。

  • --follow-symlinks: 如果提供此选项,则 SED 在就地编辑文件时会跟随符号链接。

  • -i[SUFFIX], --in-place[=SUFFIX]: 此选项用于就地编辑文件。如果提供了后缀,它会备份原始文件,否则它会覆盖原始文件。

  • -l N, --line-lenght=N: 此选项将 l 命令的行长度设置为 N 个字符。

  • --posix: 此选项禁用所有 GNU 扩展。

  • -r, --regexp-extended: 此选项允许使用扩展正则表达式而不是基本正则表达式。

  • -u, --unbuffered: 提供此选项时,SED 会从输入文件中加载最少的数据并更频繁地刷新输出缓冲区。当你不希望等待输出时,它在编辑 "tail -f" 的输出时很有用。

  • -z, --null-data: 默认情况下,SED 通过换行符分隔每一行。如果提供了 NULL-data 选项,它会通过 NULL 字符分隔行。

流编辑器 - 循环

与其他编程语言一样,SED 也提供循环和分支功能来控制执行流程。在本章中,我们将进一步探讨如何在 SED 中使用循环和分支。

SED 中的循环类似于 **goto** 语句。SED 可以跳转到标记为标签的行并继续执行其余命令。在 SED 中,我们可以如下定义 **标签**

:label 
:start 
:end 
:up

在上面的示例中,冒号 (:) 后面的名称表示标签名称。

要跳转到特定标签,我们可以使用 **b** 命令后跟标签名称。如果省略标签名称,则 SED 将跳转到 SED 文件的末尾。

让我们编写一个简单的 SED 脚本以理解循环和分支。在我们的 books.txt 文件中,有多个书籍标题及其作者的条目。以下示例将书籍标题及其作者姓名组合在一行中,用逗号分隔。然后它搜索模式 "Paulo"。如果模式匹配,它会在行的前面打印一个连字符 (-),否则它会跳转到 **Print** 标签,该标签打印该行。

[jerry]$ sed -n ' 
h;n;H;x 
s/\n/, / 
/Paulo/!b Print 
s/^/- / 
:Print 
p' 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 

乍一看,上面的脚本可能看起来很神秘。让我们来揭开这个神秘面纱。

  • 前两个命令 **h;n;H;x** 和 **s/\n/, /** 很容易理解,它们将书籍标题及其作者用逗号 (,) 分隔组合在一起。

  • 第三个命令仅在模式不匹配时跳转到标签 **Print**,否则第四个命令会执行替换。

  • :Print 只是一个标签名称,如你所知,p 是打印命令。

为了提高可读性,每个 SED 命令都放在单独一行。但是,也可以选择将所有命令放在一行,如下所示

[jerry]$ sed -n 'h;n;H;x;s/\n/, /;/Paulo/!b Print; s/^/- /; :Print;p' 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

流编辑器 - 分支

可以使用 t 命令创建分支。t 命令仅在前面的替换命令成功时才跳转到标签。让我们以上一章中的相同示例为例,但现在我们打印四个连字符而不是单个连字符 (-)。以下示例说明了 t 命令的用法。

[jerry]$ sed -n ' 
h;n;H;x 
s/\n/, / 
:Loop 
/Paulo/s/^/-/ 
/----/!t Loop 
p' 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

在上面的示例中,前两个命令是不言自明的。第三个命令定义了一个标签 Loop。第四个命令如果该行包含字符串“Paulo”,则在前面添加连字符 (-),并且 t 命令重复此过程,直到该行开头有四个连字符。

为了提高可读性,每个 SED 命令都写在单独一行。否则,我们可以编写一个单行 SED,如下所示

[jerry]$ sed -n 'h;n;H;x; s/\n/, /; :Loop;/Paulo/s/^/-/; /----/!t Loop; p' 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

流编辑器 - 模式缓冲区

我们在任何文件上执行的基本操作之一就是显示其内容。为此,我们可以使用 print 命令,它打印模式缓冲区的内容。所以让我们更多地了解模式缓冲区

首先创建一个文件,其中包含行号、书籍名称、作者和页数。在本教程中,我们将使用此文件。您可以根据自己的方便使用任何文本文件。我们的文本文件将如下所示

[jerry]$ vi 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

现在,让我们打印文件内容。

[jerry]$ sed 'p' books.txt

执行上述代码时,将产生以下结果。

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

您可能想知道为什么每行都显示两次。让我们找出原因。

您还记得 SED 的工作流程吗?默认情况下,SED 会打印模式缓冲区的内容。此外,我们在命令部分显式地包含了一个 print 命令。因此,每行都打印了两次。但不用担心。SED 有 -n 选项可以抑制模式缓冲区的默认打印。以下命令说明了这一点。

[jerry]$ sed -n 'p' 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 

恭喜!我们得到了预期的结果。默认情况下,SED 对所有行进行操作。但是,我们可以强制 SED 仅对某些行进行操作。例如,在下面的示例中,SED 仅对第 3 行进行操作。在此示例中,我们在 SED 命令之前指定了一个地址范围。

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

执行上述代码时,将产生以下结果。

3) The Alchemist, Paulo Coelho, 197 

此外,我们还可以指示 SED 仅打印某些行。例如,以下代码打印第 2 行到第 5 行的所有行。在这里,我们使用了逗号 (,) 运算符来指定地址范围。

[jerry]$ sed -n '2,5 p' books.txt 

执行上述代码时,将产生以下结果。

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

还有一个特殊字符美元符号 ($),它表示文件的最后一行。所以让我们打印文件的最后一行。

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

执行上述代码时,将产生以下结果。

6) A Game of Thrones, George R. R. Martin, 864 

但是我们也可以使用美元符号 ($) 来指定地址范围。以下示例打印从第 3 行到最后一行。

[jerry]$ sed -n '3,$ p' books.txt 

执行上述代码时,将产生以下结果。

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 

我们学习了如何使用逗号 (,) 运算符指定地址范围。SED 支持另外两个可用于指定地址范围的运算符。第一个是加号 (+) 运算符,它可以与逗号 (,) 运算符一起使用。例如,M, +n 将打印从行号 M 开始的接下来的 n 行。听起来很困惑?让我们用一个简单的例子来检查一下。以下示例打印从行号 2 开始的接下来的 4 行。

[jerry]$ sed -n '2,+4 p' books.txt 

执行上述代码时,将产生以下结果。

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 

或者,我们也可以使用波浪号 (~) 运算符指定地址范围。它使用 M~n 格式。它表示 SED 应该从行号 M 开始并处理每第 n 行。例如,50~5 匹配行号 50、55、60、65 等。让我们仅打印文件中的奇数行。

[jerry]$ sed -n '1~2 p' books.txt 

执行上述代码时,将产生以下结果。

1) A Storm of Swords, George R. R. Martin, 1216 
3) The Alchemist, Paulo Coelho, 197 
5) The Pilgrimage, Paulo Coelho, 288

以下代码仅打印文件中的偶数行。

[jerry]$ sed -n '2~2 p' books.txt 

执行上述代码时,将产生以下结果。

2) The Two Towers, J. R. R. Tolkien, 352 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
6) A Game of Thrones, George R. R. Martin, 864 

流编辑器 - 模式范围

在上一章中,我们学习了 SED 如何处理地址范围。本章介绍 SED 如何处理模式范围。模式范围可以是简单的文本或复杂的正则表达式。让我们举个例子。以下示例打印 Paulo Coelho 作者的所有书籍。

[jerry]$ sed -n '/Paulo/ p' books.txt

执行上述代码后,你将得到以下结果

3) The Alchemist, Paulo Coelho, 197 
5) The Pilgrimage, Paulo Coelho, 288

在上面的示例中,SED 对每一行进行操作,并且仅打印与字符串 Paulo 匹配的行。

我们还可以将模式范围与地址范围结合起来。以下示例打印从“炼金术士”的第一次匹配开始到第五行的行。

[jerry]$ sed -n '/Alchemist/, 5 p' books.txt

执行上述代码后,你将得到以下结果

3) The Alchemist, Paulo Coelho, 197 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
5) The Pilgrimage, Paulo Coelho, 288

我们可以使用美元符号 ($) 打印找到模式的第一次出现后的所有行。以下示例查找模式 The 的第一次出现,并立即打印文件中剩余的行

[jerry]$ sed -n '/The/,$ p' books.txt

执行上述代码后,你将得到以下结果

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 

我们还可以使用逗号 (,) 运算符指定多个模式范围。以下示例打印“Two”和“Pilgrimage”模式之间存在的所有行。

[jerry]$ sed -n '/Two/, /Pilgrimage/ p' books.txt 

执行上述代码后,你将得到以下结果

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

此外,我们可以在模式范围内使用加号 (+) 运算符。以下示例查找模式 Two 的第一次出现,并打印其后的接下来的 4 行。

[jerry]$ sed -n '/Two/, +4 p' books.txt

执行上述代码后,你将得到以下结果

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 

我们在这里只提供了一些示例,以帮助您熟悉 SED。您始终可以通过自己尝试一些示例来了解更多信息。

流编辑器 - 基本命令

本章描述了几个有用的 SED 命令。

删除命令

SED 提供各种命令来操作文本。让我们首先了解一下 delete 命令。以下是执行删除命令的方法

[address1[,address2]]d 

address1address2 分别是起始和结束地址,可以是行号或模式字符串。这两个地址都是可选参数。

顾名思义,删除命令用于执行删除操作,并且由于 SED 对行进行操作,因此我们可以说此命令用于删除行。请注意,删除命令仅从模式缓冲区中删除行;该行不会发送到输出流,并且原始文件保持不变。以下示例说明了这一点。

[jerry]$ sed 'd' books.txt 

但是输出在哪里?如果没有提供行地址,则 SED 默认情况下会对每一行进行操作。因此,它会从模式缓冲区中删除所有行。这就是为什么该命令不会在标准输出上打印任何内容的原因。

让我们指示 SED 仅对某些行进行操作。以下示例仅删除第 4 行。

[jerry]$ sed '4d' 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 
5) The Pilgrimage, Paulo Coelho, 288 
6) A Game of Thrones, George R. R. Martin, 864

此外,SED 还接受使用逗号 (,) 的地址范围。我们可以指示 SED 删除 N1 到 N2 行。例如,以下示例删除第 2 行到第 4 行的所有行。

[jerry]$ sed '2, 4 d' books.txt 

执行上述代码后,你将得到以下结果

1) A Storm of Swords, George R. R. Martin, 1216 
5) The Pilgrimage, Paulo Coelho, 288 
6) A Game of Thrones, George R. R. Martin, 864

SED 的地址范围不仅限于数字。我们还可以将模式指定为地址。以下示例删除 Paulo Coelho 作者的所有书籍。

[jerry]$ sed '/Paulo Coelho/d' books.txt 

执行上述代码后,你将得到以下结果

1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
6) A Game of Thrones, George R. R. Martin, 864 

我们还可以使用文本模式指定地址范围。以下示例删除“Storm”和“Fellowship”模式之间的所有行。

[jerry]$ sed '/Storm/,/Fellowship/d' books.txt  
5) The Pilgrimage, Paulo Coelho, 288 
6) A Game of Thrones, George R. R. Martin, 864 

除此之外,我们还可以将美元符号 ($)、加号 (+) 和波浪号 (~) 运算符与 SED 一起使用。

写入命令

我们在任何文件上执行的重要操作之一是备份,即我们创建文件的另一个副本。SED 提供 write 命令将模式缓冲区的内容存储到文件中。以下是 write 命令的语法,它类似于 delete 命令。

[address1[,address2]]w file 

这里,address1address2 分别是起始和结束地址,可以是行号或模式字符串。这两个地址都是可选参数。

在上述语法中,w 指的是 write 命令,file 是您存储内容的文件名。请注意 file 参数。当提供文件名时,如果文件不存在,则 SED 会动态创建该文件,如果文件已存在,则会覆盖该文件。

让我们使用 SED 创建文件的精确副本。请注意,wfile 之间必须恰好有一个空格。

[jerry]$ sed -n 'w books.bak' books.txt 

我们创建了另一个名为 books.bak 的文件。现在验证这两个文件是否具有相同的内容。

[jerry]$ diff books.txt books.bak  
[jerry]$ echo $?

执行上述代码后,你将得到以下结果

0

您可以假设 cp 命令执行完全相同的功能。是的!cp 命令执行相同的功能,但 SED 是一个成熟的实用程序。它允许创建一个仅包含源文件中某些行的文件。让我们将偶数行存储到另一个文件中。

[jerry]$ sed -n '2~2 w junk.txt' books.txt  
[jerry]$ cat junk.txt 

执行上述代码后,你将得到以下结果

2) The Two Towers, J. R. R. Tolkien, 352 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
6) A Game of Thrones, George R. R. Martin, 864 

您也可以将逗号 (,)、美元符号 ($) 和加号 (+) 运算符与 write 命令一起使用。

除此之外,SED 还支持模式匹配与 write 命令。假设您想将每个作者的所有书籍存储到一个单独的文件中。一种无聊且冗长的方式是手动执行此操作,而更智能的方式是使用 SED。

[jerry]$ sed -n -e '/Martin/ w Martin.txt' -e '/Paulo/ w Paulo.txt' -e '/Tolkien/ w 
Tolkien.txt' books.txt 

在上面的示例中,我们正在将每一行与模式进行匹配,并将匹配的行存储到特定文件中。这非常简单。为了指定多个命令,我们使用了 SED 命令的 -e 开关。现在让我们看看每个文件包含什么内容

[jerry]$ cat Martin.txt

执行上述代码后,你将得到以下结果

1) A Storm of Swords, George R. R. Martin, 1216 
6) A Game of Thrones, George R. R. Martin, 864

让我们显示文件内容。

[jerry]$ cat Paulo.txt

执行上述代码后,你将得到以下结果

3) The Alchemist, Paulo Coelho, 197 
5) The Pilgrimage, Paulo Coelho, 288 

让我们显示文件内容。

[jerry]$ cat Tolkien.txt

执行上述代码后,你将得到以下结果

2) The Two Towers, J. R. R. Tolkien, 352 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 

太棒了!我们得到了预期的结果。SED 确实是一个很棒的实用程序。

追加命令

任何文本编辑器最有用的操作之一就是提供追加功能。SED 通过其 append 命令支持此操作。以下是 append 的语法

[address]a\ 
Append text 

让我们在行号 4 之后追加一个新的书籍条目。以下示例显示了如何执行此操作

[jerry]$ sed '4 a 7) Adultry, Paulo Coelho, 234' 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 
7) Adultry, Paulo Coelho, 234 
5) The Pilgrimage, Paulo Coelho, 288 
6) A Game of Thrones, George R. R. Martin, 864

在命令部分,4 表示行号,a 是 append 命令,其余部分是要追加的文本。

让我们在文件的末尾插入一行文本。为此,请使用 $ 作为地址。以下示例说明了这一点

[jerry]$ sed '$ a 7) Adultry, Paulo Coelho, 234' 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 
7) Adultry, Paulo Coelho, 234 

除了行号之外,我们还可以使用文本模式指定地址。例如,以下示例在匹配字符串 The Alchemist 后追加文本。

[jerry]$ sed '/The Alchemist/ a 7) Adultry, Paulo Coelho, 234' 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 
7) Adultry, Paulo Coelho, 234 
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 

请注意,如果有多个模式匹配,则会在每次匹配后追加文本。以下示例说明了这种情况。

[jerry]$ sed '/The/ a 7) Adultry, Paulo Coelho, 234' books.txt 

执行上述代码后,你将得到以下结果

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

更改命令

SED 提供 changereplace 命令,用 c 表示。此命令有助于用新文本替换现有行。当提供行范围时,所有行都将作为一个组被替换为单个文本行。以下是更改命令的语法

[address1[,address2]]c\ 
Replace text

让我们用其他一些文本替换第三行。

[jerry]$ sed '3 c 3) Adultry, Paulo Coelho, 324' books.txt

执行上述代码后,你将得到以下结果

1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352 
3) Adultry, Paulo Coelho, 324 
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

SED 也接受模式作为地址。在下面的示例中,当模式匹配成功时,替换一行。

[jerry]$ sed '/The Alchemist/ c 3) Adultry, Paulo Coelho, 324' books.txt

执行上述代码后,你将得到以下结果

1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352 
3) Adultry, Paulo Coelho, 324 
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 

SED 还允许用一行替换多行。下面的示例删除第四行到第六行,并用新文本替换它们。

[jerry]$ sed '4, 6 c 4) Adultry, Paulo Coelho, 324' 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) Adultry, Paulo Coelho, 324

插入命令

插入命令的工作方式与追加命令非常相似。唯一的区别在于它在特定位置之前插入一行。下面给出的是插入命令的语法

[address]i\ 
Insert text 

让我们用一些例子来理解插入命令。以下命令在第四行之前插入一个新条目。

[jerry]$ sed '4 i 7) Adultry, Paulo Coelho, 324' 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 
7) Adultry, Paulo Coelho, 324 
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

在上面的示例中,4 是位置编号,i 表示插入命令,其余部分是要插入的文本。

要在文件开头插入文本,请将行地址提供为1。以下命令说明了这一点

[jerry]$ sed '1 i 7) Adultry, Paulo Coelho, 324' books.txt

执行上述代码后,你将得到以下结果

7) Adultry, Paulo Coelho, 324 
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

此外,我们可以插入多行。以下命令在最后一行之前插入两行。

[jerry]$ sed '$ i 7) Adultry, Paulo Coelho, 324

执行上述代码后,你将得到以下结果

8) Eleven Minutes, Paulo Coelho, 304' 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 
7) Adultry, Paulo Coelho, 324 
8) Eleven Minutes, Paulo Coelho, 304 
6) A Game of Thrones, George R. R. Martin, 864

请注意,要插入的条目在单独的行上输入,并以反斜杠 (\) 字符分隔。

转换命令

SED 提供了一个转换字符的命令,它表示为y。它按位置转换字符。下面给出的是转换命令的语法

[address1[,address2]]y/list-1/list-2/

请注意,转换基于字符从列表 1中的位置到列表 2中相同位置的字符,并且两个列表都必须是显式字符列表。不支持正则表达式和字符类。此外,列表 1列表 2的大小必须相同。

以下示例将阿拉伯数字转换为罗马数字。

[jerry]$ echo "1 5 15 20" | sed 'y/151520/IVXVXX/' 

执行上述代码后,你将得到以下结果

I V IV XX 

l 命令

仅通过查看它们,你能区分空格分隔的单词和仅由制表符分隔的单词吗?当然不能。但是 SED 可以为你做到这一点。SED 使用l命令显示文本中的隐藏字符。例如,制表符用\t表示,行尾用$字符表示。下面给出的是l命令的语法。

[address1[,address2]]l 
[address1[,address2]]l [len] 

让我们创建一个包含制表符的文件进行演示。为简单起见,我们将使用相同的文件,只是用制表符替换空格。等等!但是如何做到这一点——通过在文本编辑器中打开文件并将每个空格替换为制表符?当然不是!我们可以利用 SED 命令来实现这一点。

[jerry]$ sed 's/ /\t/g' books.txt > junk.txt 

现在让我们使用l命令显示隐藏字符

[jerry]$ sed -n 'l' junk.txt

执行上述代码后,你将得到以下结果

1)\tA\tStorm\tof\tSwords,George\tR.\tR.\tMartin,1216$ 
2)\tThe\tTwo\tTowers,J.\tR.\tR.\tTolkien,352$ 
3)\tThe\tAlchemist,Paulo\tCoelho,197$ 
4)\tThe\tFellowship\tof\tthe\tRing,J.\tR.\tR.\tTolkien,432$ 
5)\tThe\tPilgrimage,Paulo\tCoelho,288$ 
6)\tA\tGame\tof\tThrones,George\tR.\tR.\tMartin\t,864$

与其他 SED 命令一样,它也接受行号和模式作为地址。您可以自己尝试。

让我们仔细看看 SED 的另一个有趣功能。我们可以指示 SED 在特定数量的字符后执行换行。以下示例在 25 个字符后换行。

[jerry]$ sed -n 'l 25' books.txt

执行上述代码后,你将得到以下结果

1) A Storm of Swords,Geo\ 
rge R. R. Martin,1216$ 
2) The Two Towers,J. R. \ 
R. Tolkien,352$ 
3) The Alchemist,Paulo C\ 
oelho,197$ 
4) The Fellowship of the\ 
 Ring,J. R. R. Tolkien,4\ 
32$ 
5) The Pilgrimage,Paulo \ 
Coelho,288$ 
6) A Game of Thrones,Geo\ 
rge R. R. Martin ,864$

请注意,在上面的示例中,换行限制是在 l 命令之后提供的。在这种情况下,它是 25 个字符。此选项是 GNU 特定的,可能不适用于其他 SED 变体。

换行限制为 0 表示除非有换行符,否则永远不会换行。以下简单命令说明了这一点。

[jerry]$ sed -n 'l 0' 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$ 

退出命令

退出命令指示 SED 退出当前执行流程。它由q命令表示。下面给出的是退出命令的语法

[address]q 
[address]q [value]

请注意,退出命令不接受地址范围,它只支持单个地址。默认情况下,SED 遵循读取、执行和重复的工作流程;但是当遇到退出命令时,它只是停止当前执行。

让我们打印文件中的前 3 行。

[jerry]$ sed '3 q' 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

除了行号之外,我们还可以使用文本模式。以下命令在模式匹配成功时退出。

[jerry]$ sed '/The Alchemist/ q' 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

除此之外,SED 还可以接受一个,该值可用作退出状态。以下命令将其退出状态显示为 100。

[jerry]$ sed '/The Alchemist/ q 100' 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

现在让我们验证退出状态。

[jerry]$ echo $? 

执行上述代码后,你将得到以下结果

100

读取命令

我们可以指示 SED 读取文件的内容并在特定条件匹配时显示它们。该命令由字母r表示。下面给出的是读取命令的语法。

[address]r file

请注意,r命令和文件名之间必须恰好有一个空格。

让我们用一个简单的例子来理解它。创建一个名为junk.txt的示例文件。

[jerry]$ echo "This is junk text." > junk.txt 

以下命令指示 SED 读取junk.txt的内容并在第三行之后插入它们。

[jerry]$ sed '3 r junk.txt' 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 
This is junk text. 
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

在上面的示例中,3 表示行地址,r是命令名称,junk.txt是要显示其内容的文件名。此外,GNU SED 还接受地址范围。例如,以下命令在第三、第四和第五行之后插入junk.txt的内容。

[jerry]$ sed '3, 5 r junk.txt' 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 
This is junk text. 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
This is junk text. 
5) The Pilgrimage, Paulo Coelho, 288 
This is junk text. 
6) A Game of Thrones, George R. R. Martin, 864

与其他 SED 命令一样,读取命令也接受模式作为地址。例如,以下命令在模式匹配成功时插入junk.txt的内容。

[jerry]$ sed '/Paulo/ r junk.txt' 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 
This is junk text. 
4) The Fellowship of the Ring, J. R. R. Tolkien, 432 
5) The Pilgrimage, Paulo Coelho, 288 
This is junk text. 
6) A Game of Thrones, George R. R. Martin, 864 

执行命令

我们可以使用执行命令从 SED 执行外部命令。它由e表示。下面给出的是执行命令的语法。

[address1[,address2]]e [command]

让我们用一个简单的例子来说明执行命令。以下 SED 命令在第三行之前执行 UNIXdate命令。

[jerry]$ sed '3 e date' books.txt

执行上述代码后,你将得到以下结果

1) A Storm of Swords, George R. R. Martin, 1216 
2) The Two Towers, J. R. R. Tolkien, 352 
Sun Sep  7 18:04:49 IST 2014 
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

与其他命令一样,它也接受模式作为地址。例如,以下示例在模式匹配成功时执行date命令。请注意,在每次模式匹配之后,首先执行命令,然后显示模式缓冲区的内容。

[jerry]$ sed '/Paulo/ e date' books.txt 

执行上述代码后,你将得到以下结果

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

如果你仔细观察e命令的语法,你会注意到命令是可选的。当e后面没有提供命令时,它会将模式缓冲区的内容视为外部命令。为了说明这一点,让我们创建一个包含一些简单命令的commands.txt文件。

[jerry]$ echo -e "date\ncal\nuname" > commands.txt 
[jerry]$ cat commands.txt

执行上述代码后,你将得到以下结果

date 
cal 
uname

文件中的命令是不言自明的。在e后面没有命令的情况下,SED 会依次执行所有这些命令。以下简单示例说明了这一点。

[jerry]$ sed 'e' commands.txt 

执行上述代码后,你将得到以下结果

Sun Sep  7 18:14:20 IST 2014 
   September 2014      
Su Mo Tu We Th Fr Sa   
    1  2  3  4  5  6   
 7  8  9 10 11 12 13   
14 15 16 17 18 19 20   
21 22 23 24 25 26 27   
28 29 30               
                       
Linux 

与其他 SED 命令一样,执行命令也接受所有有效的地址范围。

其他命令

默认情况下,SED 对单行进行操作,但它也可以对多行进行操作。多行命令用大写字母表示。例如,与n命令不同,N命令不会清除和打印模式空间。相反,它在当前模式空间的末尾添加一个换行符 (\n),并将输入文件中的下一行追加到当前模式空间,并通过执行其余的 SED 命令继续 SED 的标准流程。下面给出的是N命令的语法。

[address1[,address2]]N

让我们打印书籍标题及其相应作者的逗号分隔列表。以下示例说明了这一点。

[jerry]$ sed 'N; s/\n/, /g' 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

让我们了解上面的示例是如何工作的。N命令将第一行,即A Storm of Swords读入模式缓冲区,并追加\n后跟下一行。模式空间现在包含A Storm of Swords\nGeorge R. R. Martin。在下一步中,我们将换行符替换为逗号。

p命令一样,我们有一个P命令来打印由N命令创建的多行模式空间的第一部分(直到嵌入的换行符)。下面给出的是P命令的语法,它类似于p命令。

[address1[,address2]]P 

在前面的示例中,我们看到N命令创建了一个换行符分隔的书籍标题及其作者列表。让我们只打印它的第一部分,即只有书籍的标题。以下命令说明了这一点。

[jerry]$ sed -n 'N;P' books.txt

执行上述代码后,你将得到以下结果

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

请注意,在没有N的情况下,它的行为与p命令相同。以下简单命令说明了这种情况。

[jerry]$ sed -n 'P' 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

除此之外,SED 还提供了一个v命令来检查版本。如果提供的版本大于已安装的 SED 版本,则命令执行失败。请注意,此选项是 GNU 特定的,可能不适用于其他 SED 变体。下面给出的是v命令的语法。

[address1[,address2]]v [version]

首先,找出当前的 SED 版本。

[jerry]$ sed --version 

执行上述代码后,你将得到以下结果

sed (GNU sed) 4.2.2 

在下面的示例中,SED 版本大于版本 4.2.2,因此 SED 命令中止其执行。

[jerry]$ sed 'v 4.2.3' books.txt 

执行上述代码后,你将得到以下结果

sed: -e expression #1, char 7: expected newer version of sed

但是,如果提供的版本小于或等于版本 4.2.2,则命令按预期工作。

[jerry]$ sed 'v 4.2.2' 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

流编辑器 - 特殊字符

SED 提供了两个被视为命令的特殊字符。本章说明了这两个特殊字符的用法。

= 命令

“=”命令处理行号。下面给出的是“=”命令的语法

[/pattern/]= 
[address1[,address2]]=

“=”命令将行号及其内容写入标准输出流。以下示例说明了这一点。

[jerry]$ sed '=' books.txt 

执行上述代码后,你将得到以下结果

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

让我们打印前四行的行号和内容。以下命令打印带有行号的前四行,其余的行不带行号。

[jerry]$ sed '1, 4=' books.txt 

执行上述代码后,你将得到以下结果

1 
1) A Storm of Swords, George R. R. Martin, 1216 
2 
2) The Two Towers, J. R. R. Tolkien, 352 
3 
3) The Alchemist, Paulo Coelho, 197 
4 
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

此外,我们可以指示 SED 在模式匹配成功时打印行号。以下示例打印包含模式“Paulo”的行号。

[jerry]$ sed '/Paulo/ =' books.txt 

执行上述代码后,你将得到以下结果

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

你能猜出以下 SED 命令的功能吗?

[jerry]$ sed -n '$ =' books.txt

执行上述代码后,你将得到以下结果

6 

是的,你答对了。它计算文件中存在的总行数。让我们揭开代码的神秘面纱。在命令部分,我们使用了“$ =”,它打印最后一行及其内容的行号。但我们也提供了-n标志,它抑制了模式缓冲区的默认打印。因此,只显示最后一行号。

& 命令

SED 支持特殊字符&。每当模式匹配成功时,此特殊字符都会存储匹配的模式。它通常与替换命令一起使用。让我们看看如何利用这个高效的功能。

book.txt 文件中的每一行都编号。让我们在每行的开头添加Book number字样。以下示例说明了这一点。

[jerry]$ sed 's/[[:digit:]]/Book number &/' books.txt

执行上述代码后,你将得到以下结果

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

这个例子非常简单。首先,我们搜索第一个数字的出现位置,它就是行号(这就是我们使用[[:digit:]]的原因),并且SED会自动将匹配的模式存储在特殊字符&中。在第二步中,我们在每个匹配模式之前插入单词Book number,也就是在每一行之前。

让我们看另一个例子。在book.txt文件中,最后一个数字表示书籍的页数。让我们在前面添加“Pages =”。为此,请查找数字的最后一次出现,并将其替换为“Pages = &”。这里,&存储匹配的模式,即页码。

[jerry]$ sed 's/[[:digit:]]*$/Pages = &/' books.txt 

执行上述语法后,您将获得以下结果。

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

目前,只需记住[[:digit:]]*$查找数字的最后一次出现。在“正则表达式”章节中,我们将更深入地探讨正则表达式。

流编辑器 - 字符串

替换命令

像“查找和替换”这样的文本替换操作在任何文本编辑器中都很常见。在本节中,我们将说明SED如何执行文本替换。下面是替换命令的语法。

[address1[,address2]]s/pattern/replacement/[flags]

这里,address1address2分别是起始和结束地址,可以是行号或模式字符串。这两个地址都是可选参数。模式是我们想要替换的文本,替换字符串用于替换。此外,我们还可以使用SED指定可选标志。

在books.txt文件中,我们使用逗号(,)分隔每一列。让我们使用竖线(|)分隔每一列。为此,请将逗号(,)替换为竖线(|)。

[jerry]$ sed 's/,/ | /' 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 

如果您仔细观察,只会替换第一个逗号,第二个逗号保持不变。为什么?一旦模式匹配,SED就会用替换字符串替换它并移动到下一行。默认情况下,它只替换第一个匹配项。要替换所有匹配项,请使用全局标志(g)与SED,如下所示。

[jerry]$ sed 's/,/ | /g' 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

现在所有逗号(,)都被替换为竖线(|)。

我们可以指示SED仅在模式匹配成功时执行文本替换。以下示例仅当一行包含模式The Pilgrimage时,才将逗号(,)替换为竖线(|)。

[jerry]$ sed '/The Pilgrimage/ s/,/ | /g' 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

除此之外,SED还可以替换模式的特定出现次数。让我们只用竖线(|)替换逗号(,)的第二个实例。

[jerry]$ sed 's/,/ | /2' 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

在上面的例子中,SED命令末尾(或标志的位置)的数字表示第二个匹配项。

SED提供了一个有趣的特性。执行替换后,SED提供了一个选项,只显示已更改的行。为此,SED使用p标志,表示打印。以下示例仅列出已更改的行。

[jerry]$ sed -n 's/Paulo Coelho/PAULO COELHO/p' books.txt

执行上述代码后,你将得到以下结果

3) The Alchemist, PAULO COELHO, 197 
5) The Pilgrimage, PAULO COELHO, 288 

我们也可以将更改的行存储在另一个文件中。要实现此结果,请使用w标志。以下示例显示了如何操作。

[jerry]$ sed -n 's/Paulo Coelho/PAULO COELHO/w junk.txt' books.txt

我们使用了相同的SED命令。让我们验证junk.txt文件的内容。

[jerry]$ cat junk.txt

执行上述代码后,你将得到以下结果

3) The Alchemist, PAULO COELHO, 197 
5) The Pilgrimage, PAULO COELHO, 288

要执行不区分大小写的替换,请使用i标志,表示忽略大小写。以下示例执行不区分大小写的替换。

[jerry]$ sed  -n 's/pAuLo CoElHo/PAULO COELHO/pi' books.txt

执行上述代码后,你将得到以下结果

3) The Alchemist, PAULO COELHO, 197 
5) The Pilgrimage, PAULO COELHO, 288

到目前为止,我们只使用了斜杠(/)字符作为分隔符,但我们也可以使用竖线(|)、at符号(@)、脱字符(^)、感叹号(!)作为分隔符。以下示例显示了如何使用其他字符作为分隔符。

假设您需要将路径/bin/sed替换为/home/jerry/src/sed/sed-4.2.2/sed。因此,您的SED命令如下所示。

[jerry]$ echo "/bin/sed" | sed 's/\/bin\/sed/\/home\/jerry\/src\/sed\/sed-4.2.2\/sed/'

执行上述代码后,你将得到以下结果

/home/jerry/src/sed/sed-4.2.2/sed

我们可以使此命令更易读且易于理解。让我们使用竖线(|)作为分隔符,并查看结果。

[jerry]$ echo "/bin/sed" | sed 's|/bin/sed|/home/jerry/src/sed/sed-4.2.2/sed|'

执行上述代码后,你将得到以下结果

/home/jerry/src/sed/sed-4.2.2/sed

确实!我们得到了相同的结果,并且语法更易读。类似地,我们可以使用“at”符号(@)作为分隔符,如下所示。

[jerry]$ echo "/bin/sed" | sed 's@/bin/sed@/home/jerry/src/sed/sed-4.2.2/sed@'

执行上述代码后,你将得到以下结果

/home/jerry/src/sed/sed-4.2.2/sed 

除此之外,我们还可以使用脱字符(^)作为分隔符。

[jerry]$ echo "/bin/sed" | sed 's^/bin/sed^/home/jerry/src/sed/sed-4.2.2/sed^'

执行上述代码后,你将得到以下结果

/home/jerry/src/sed/sed-4.2.2/sed 

我们还可以使用感叹号(!)作为分隔符,如下所示。

[jerry]$ echo "/bin/sed" | sed 's!/bin/sed!/home/jerry/src/sed/sed-4.2.2/sed!'

执行上述代码后,你将得到以下结果

/home/jerry/src/sed/sed-4.2.2/sed 

通常,反斜杠(/)用作分隔符,但有时使用SED支持的其他分隔符会更方便。

创建子字符串

我们学习了强大的替换命令。让我们看看是否可以从匹配的文本中找到子字符串。让我们通过一个例子来理解如何做到这一点。

让我们考虑以下文本。

[jerry]$ echo "Three One Two"

假设我们必须将其排列成一个序列。意思是,它应该先打印One,然后是Two,最后是Three。以下单行代码可以满足需求。

echo "Three One Two" | sed 's|\(\w\+\) \(\w\+\) \(\w\+\)|\2 \3 \1|'

请注意,在上面的示例中,竖线(|)用作分隔符。

在SED中,可以使用分组运算符指定子字符串,并且必须以转义字符为前缀,即\(\)

\w是一个正则表达式,匹配任何字母、数字或下划线,并且“+”用于匹配多个字符。换句话说,正则表达式\(\w\+\)匹配输入字符串中的单个单词。

在输入字符串中,有三个单词用空格分隔,因此有三个用空格分隔的正则表达式。第一个正则表达式存储第一个单词,即Three,第二个存储单词One,第三个存储单词Two

这些子字符串由\N引用,其中N是子字符串编号。因此,\2打印第二个子字符串,即One;\3打印第三个子字符串,即Two;以及\1打印第一个子字符串,即Three

让我们用逗号(,)分隔这些单词,并相应地修改正则表达式。

[jerry]$ echo "Three,One,Two" | sed 's|\(\w\+\),\(\w\+\),\(\w\+\)|\2,\3,\1|'

执行上述代码后,你将得到以下结果

One,Two,Three

请注意,现在正则表达式中使用逗号(,)代替空格。

字符串替换标志(仅限GNU SED)

在上一节中,我们看到了替换命令的一些示例。GNU SED提供了一些特殊的转义序列,这些序列可以在替换字符串中使用。请注意,这些字符串替换标志是GNU特有的,可能不适用于其他版本的SED。这里我们将讨论字符串替换标志。

  • \L:当在替换字符串中指定\L时,它会将\L之后单词的其余所有字符都视为小写字符。例如,字符“ULO”被视为小写字符。

[jerry]$ sed -n 's/Paulo/PA\LULO/p' books.txt

执行上述代码后,你将得到以下结果

3) The Alchemist, PAulo Coelho, 197
5) The Pilgrimage, PAulo Coelho, 288
  • \u:当在替换字符串中指定\u时,它会将\u之后的第一个字符视为大写字符。在下面的示例中,\u用于字符'a'和'o'之前。因此,SED将这些字符视为大写字母。

[jerry]$ sed -n 's/Paulo/p\uaul\uo/p' books.txt

执行上述代码后,你将得到以下结果

3) The Alchemist, pAulO Coelho, 197 
5) The Pilgrimage, pAulO Coelho, 288
  • \U:当在替换字符串中指定\U时,它会将\U之后单词的其余所有字符都视为大写字符。

[jerry]$ sed -n 's/Paulo/\Upaulo/p' books.txt 

执行上述代码后,你将得到以下结果

3) The Alchemist, PAULO Coelho, 197 
5) The Pilgrimage, PAULO Coelho, 288
  • \E:此标志应与\L或\U一起使用。它停止由标志\L或\U启动的转换。在下面的示例中,只有第一个单词被替换为大写字母。

[jerry]$ sed -n 's/Paulo Coelho/\Upaulo \Ecoelho/p' books.txt

执行上述代码后,你将得到以下结果

3) The Alchemist, PAULO coelho, 197 
5) The Pilgrimage, PAULO coelho, 288

流编辑器 - 模式管理

我们已经讨论了模式和保持缓冲区的用法。在本章中,我们将进一步探讨它们的用法。让我们讨论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。由于模式匹配成功,因此它将模式缓冲区的内容与保持缓冲区的内容交换。现在模式缓冲区包含The Alchemist,保持缓冲区包含Paulo Coelho。最后,它打印模式缓冲区的内容。相同的步骤应用于模式The Pilgrimage。

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,然后将保持缓冲区复制到模式缓冲区。因此,模式缓冲区和保持缓冲区都包含 The Alchemist。最后,它打印模式缓冲区的内容。相同的步骤应用于模式 The Pilgrimage。

类似地,我们可以将保持缓冲区的内容追加到模式缓冲区。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 

流编辑器 - 正则表达式

正是正则表达式使 SED 变得强大而高效。许多复杂的任务都可以用正则表达式来解决。任何命令行专家都知道正则表达式的强大功能。

与许多其他 GNU/Linux 实用程序一样,SED 也支持正则表达式,通常称为regex。本章详细介绍了正则表达式。本章分为三个部分:标准正则表达式、POSIX 正则表达式类和元字符。

标准正则表达式

行首 (^)

在正则表达式术语中,插入符号 (^) 符号匹配行的开头。以下示例打印所有以模式“The”开头的行。

[jerry]$ sed -n '/^The/ p' books.txt

执行上述代码后,你将得到以下结果

The Two Towers, J. R. R. Tolkien 
The Alchemist, Paulo Coelho 
The Fellowship of the Ring, J. R. R. Tolkien 
The Pilgrimage, Paulo Coelho

行尾 ($)

行尾由美元符号 ($) 表示。以下示例打印以“Coelho”结尾的行。

[jerry]$ sed -n '/Coelho$/ p' books.txt 

执行上述代码后,你将得到以下结果

The Alchemist, Paulo Coelho 
The Pilgrimage, Paulo Coelho

单个字符 (.)

点 (.) 匹配除行尾字符之外的任何单个字符。以下示例打印所有以字符“t”结尾的三个字母的单词。

[jerry]$ echo -e "cat\nbat\nrat\nmat\nbatting\nrats\nmats" | sed -n '/^..t$/p' 

执行上述代码后,你将得到以下结果

cat 
bat 
rat 
mat

匹配字符集 ([])

在正则表达式术语中,字符集由方括号 ([]) 表示。它用于匹配多个字符中的一个。以下示例匹配模式“Call”和“Tall”,但不匹配“Ball”。

[jerry]$ echo -e "Call\nTall\nBall" | sed -n '/[CT]all/ p'

执行上述代码后,你将得到以下结果

Call 
Tall

排除集 ([^])

在排除集中,插入符号否定方括号中字符的集合。以下示例仅打印“Ball”。

[jerry]$ echo -e "Call\nTall\nBall" | sed -n '/[^CT]all/ p'

执行上述代码后,你将得到以下结果

Ball 

字符范围 ([-])

当提供字符范围时,正则表达式匹配方括号中指定的范围内的任何字符。以下示例匹配“Call”和“Tall”,但不匹配“Ball”。

[jerry]$ echo -e "Call\nTall\nBall" | sed -n '/[C-Z]all/ p' 

执行上述代码后,你将得到以下结果

Call 
Tall

现在让我们将范围修改为“A-P”并观察结果。

[jerry]$ echo -e "Call\nTall\nBall" | sed -n '/[A-P]all/ p' 

执行上述代码后,你将得到以下结果

Call 
Ball

零次或一次出现 (\?)

在 SED 中,问号 (\?) 匹配前一个字符的零次或一次出现。以下示例匹配“Behaviour”和“Behavior”。在这里,我们使用“\?”将“u”作为可选字符。

[jerry]$ echo -e "Behaviour\nBehavior" | sed -n '/Behaviou\?r/ p' 

执行上述代码后,你将得到以下结果

Behaviour 
Behavior

一次或多次出现 (\+)

在 SED 中,加号 (+) 匹配前一个字符的一次或多次出现。以下示例匹配“2”的一次或多次出现。

[jerry]$ echo -e "111\n22\n123\n234\n456\n222"  | sed -n '/2\+/ p'

执行上述代码后,你将得到以下结果

22 
123 
234 
222 

零次或多次出现 (*)

星号 (*) 匹配前一个字符的零次或多次出现。以下示例匹配“ca”、“cat”、“catt”等。

[jerry]$ echo -e "ca\ncat" | sed -n '/cat*/ p' 

执行上述代码后,你将得到以下结果

ca 
cat 

恰好 N 次出现 {n}

{n} 恰好匹配前一个字符的“n”次出现。以下示例仅打印三位数。但在那之前,您需要创建以下仅包含数字的文件。

[jerry]$ cat numbers.txt 

执行上述代码后,你将得到以下结果

1 
10 
100 
1000 
10000 
100000 
1000000 
10000000 
100000000 
1000000000

让我们编写 SED 表达式。

[jerry]$ sed -n '/^[0-9]\{3\}$/ p' numbers.txt 

执行上述代码后,你将得到以下结果

100

请注意,花括号对由“\”字符转义。

至少 n 次出现 {n,}

{n,} 匹配前一个字符的至少“n”次出现。以下示例打印所有大于或等于五位的数字。

[jerry]$ sed -n '/^[0-9]\{5,\}$/ p' numbers.txt

执行上述代码后,你将得到以下结果

10000 
100000 
1000000
10000000 
100000000 
1000000000 

M 到 N 次出现 {m, n}

{m, n} 匹配前一个字符的至少“m”次,最多“n”次出现。以下示例打印所有至少有五位数但不多于八位数的数字。

[jerry]$ sed -n '/^[0-9]\{5,8\}$/ p' numbers.txt

执行上述代码后,你将得到以下结果

10000 
100000 
1000000 
10000000 

管道 (|)

在 SED 中,管道字符的行为类似于逻辑或运算。它匹配管道两侧的项目。以下示例匹配“str1”或“str3”。

[jerry]$ echo -e "str1\nstr2\nstr3\nstr4" | sed -n '/str\(1\|3\)/ p' 

执行上述代码后,你将得到以下结果

str1 
str3

请注意,括号对和管道 (|) 由“\”字符转义。

转义字符

某些特殊字符。例如,换行符由“\n”表示,回车符由“\r”表示,等等。要在常规 ASCII 上下文中使用这些字符,我们必须使用反斜杠 (\) 字符对其进行转义。本章说明了特殊字符的转义。

转义“\”

以下示例匹配模式“\”。

[jerry]$ echo 'str1\str2' | sed -n '/\\/ p'

执行上述代码后,你将得到以下结果

str1\str2 

转义“\n”

以下示例匹配换行符。

[jerry]$ echo 'str1\nstr2' | sed -n '/\\n/ p'

执行上述代码后,你将得到以下结果

str1\nstr2

转义“\r”

以下示例匹配回车符。

[jerry]$ echo 'str1\rstr2' | sed -n '/\\r/ p'

执行上述代码后,你将得到以下结果

str1\rstr2

转义“\dnnn”

这匹配十进制 ASCII 值为“nnn”的字符。以下示例仅匹配字符“a”。

[jerry]$ echo -e "a\nb\nc" | sed -n '/\d97/ p'

执行上述代码后,你将得到以下结果

a

转义“\onnn”

这匹配八进制 ASCII 值为“nnn”的字符。以下示例仅匹配字符“b”。

[jerry]$ echo -e "a\nb\nc" | sed -n '/\o142/ p' 

执行上述代码后,你将得到以下结果

b 

这匹配十六进制 ASCII 值为“nnn”的字符。以下示例仅匹配字符“c”。

[jerry]$ echo -e "a\nb\nc" | sed -n '/\x63/ p'

执行上述代码后,你将得到以下结果

c

POSIX 正则表达式类

某些保留字具有特殊含义。这些保留字称为 POSIX 正则表达式类。本节介绍 SED 支持的 POSIX 类。

[:alnum:]

它表示字母数字字符。以下示例仅匹配“One”和“123”,但不匹配制表符。

[jerry]$ echo -e "One\n123\n\t" | sed -n '/[[:alnum:]]/ p'

执行上述代码后,你将得到以下结果

One 
123

[:alpha:]

它仅表示字母字符。以下示例仅匹配单词“One”。

[jerry]$ echo -e "One\n123\n\t" | sed -n '/[[:alpha:]]/ p'

执行上述代码后,你将得到以下结果

One 

[:blank:]

它表示空格字符,可以是空格或制表符。以下示例仅匹配制表符。

[jerry]$ echo -e "One\n123\n\t" | sed -n '/[[:space:]]/ p' | cat -vte

执行上述代码后,你将得到以下结果

^I$

请注意,命令“cat -vte”用于显示制表符 (^I)。

[:digit:]

它仅表示十进制数字。以下示例仅匹配数字“123”。

[jerry]$ echo -e "abc\n123\n\t" | sed -n '/[[:digit:]]/ p' 

执行上述代码后,你将得到以下结果

123 

[:lower:]

它仅表示小写字母。以下示例仅匹配“one”。

[jerry]$ echo -e "one\nTWO\n\t" | sed -n '/[[:lower:]]/ p' 

执行上述代码后,你将得到以下结果

one 

[:upper:]

它仅表示大写字母。以下示例仅匹配“TWO”。

[jerry]$ echo -e "one\nTWO\n\t" | sed -n '/[[:upper:]]/ p'

执行上述代码后,你将得到以下结果

TWO

[:punct:]

它表示标点符号,包括非空格或字母数字字符

[jerry]$ echo -e "One,Two\nThree\nFour" | sed -n '/[[:punct:]]/ p'

执行上述代码后,你将得到以下结果

One,Two

[:space:]

它表示空格字符。以下示例说明了这一点。

[jerry]$ echo -e "One\n123\f\t" | sed -n '/[[:space:]]/ p' | cat -vte 

执行上述代码后,你将得到以下结果

123^L^I$ 

元字符

与传统正则表达式一样,SED 也支持元字符。这些是 Perl 样式的正则表达式。请注意,元字符支持是 GNU SED 特定的,可能不适用于其他 SED 变体。让我们详细讨论元字符。

词边界 (\b)

在正则表达式术语中,“\b”匹配词边界。例如,“\bthe\b”匹配“the”,但不匹配“these”、“there”、“they”、“then”等。以下示例说明了这一点。

[jerry]$ echo -e "these\nthe\nthey\nthen" | sed -n '/\bthe\b/ p'

执行上述代码后,你将得到以下结果

the

非词边界 (\B)

在正则表达式术语中,“\B”匹配非词边界。例如,“the\B”匹配“these”和“they”,但不匹配“the”。以下示例说明了这一点。

[jerry]$ echo -e "these\nthe\nthey" | sed -n '/the\B/ p'

执行上述代码后,你将得到以下结果

these 
they

单个空格 (\s)

在 SED 中,“\s”表示单个空格字符。以下示例匹配“Line\t1”,但不匹配“Line1”。

[jerry]$ echo -e "Line\t1\nLine2" | sed -n '/Line\s/ p'

执行上述代码后,你将得到以下结果

Line 1 

单个非空格 (\S)

在 SED 中,“\S”表示单个空格字符。以下示例匹配“Line2”,但不匹配“Line\t1”。

[jerry]$ echo -e "Line\t1\nLine2" | sed -n '/Line\S/ p' 

执行上述代码后,你将得到以下结果

Line2

单个单词字符 (\w)

在 SED 中,“\w”表示单个单词字符,即字母字符、数字和下划线 (_) 。以下示例说明了这一点。

[jerry]$ echo -e "One\n123\n1_2\n&;#" | sed -n '/\w/ p'

执行上述代码后,你将得到以下结果

One 
123 
1_2

单个非单词字符 (\W)

在 SED 中,“\W”表示单个非单词字符,这与“\w”完全相反。以下示例说明了这一点。

[jerry]$ echo -e "One\n123\n1_2\n&;#" | sed -n '/\W/ p'

执行上述代码后,你将得到以下结果

&;#

模式空间的开头 (\`)

在 SED 中,“\`”表示模式空间的开头。以下示例仅匹配单词“One”。

[jerry]$ echo -e "One\nTwo One" | sed -n '/\`One/ p' 

执行上述代码后,你将得到以下结果

One

流编辑器 - 有用技巧

SED 是一款非常棒的实用程序,它允许通过多种方式解决问题。这是 UNIX 方式,SED 完美地证明了这一点。GNU/Linux 提供了许多有用的实用程序来执行日常任务。让我们使用 SED 模拟一些实用程序。有时,我们似乎正在以困难的方式解决一个简单的问题,但目的只是为了展示 SED 的强大功能。

Cat 命令

在以下示例中,每一行都作为默认工作流程的一部分打印。

[jerry]$ sed '' 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 

以下示例使用 print 命令显示文件内容。

[jerry]$ sed -n 'p' 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]$ echo -e "Line #1\n\n\nLine #2" | sed '/^$/d'

执行上述代码后,你将得到以下结果

Line #1 
Line #2 

类似地,以下示例仅在行非空时打印该行。

[jerry]$ echo -e "Line #1\n\n\nLine #2" | sed -n '/^$/!p'

执行上述代码后,你将得到以下结果

Line #1 
Line #2

从 C++ 程序中删除注释行

让我们创建一个示例 C++ 程序。

#include <iostream> 
using namespace std; 

int main(void) 
{ 
   // Displays message on stdout. 
   cout >> "Hello, World !!!" >> endl;  
   return 0; // Return success. 
}

现在使用以下正则表达式删除注释。

[jerry]$ sed 's|//.*||g' hello.cpp

执行上述代码后,你将得到以下结果

#include <iostream>
using namespace std; 

int main(void) 
{ 
   cout >> "Hello, World !!!" >> endl; 
   return 0;  
} 

在特定行之前添加注释

以下示例在第 3 到第 5 行之前添加注释。

[jerry]$ sed '3,5 s/^/#/' hello.sh 

执行上述代码后,你将得到以下结果

#!/bin/bash 
#pwd 
#hostname 
#uname -a 
who 
who -r 
lsb_release -a

Wc -l 命令

"wc -l" 命令计算文件中存在的行数。以下 SED 表达式模拟了相同的功能。

[jerry]$ sed -n '$ =' hello.sh 

执行上述代码后,你将得到以下结果

8 

Head 命令

默认情况下,head 命令打印文件的头 10 行。让我们用 SED 模拟相同的行为。

[jerry]$ sed '10 q' 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

Tail -1 命令

"tail -1" 打印文件的最后一行。以下语法显示了它的模拟。

[jerry]$ echo -e "Line #1\nLine #2" > test.txt 
[jerry]$ cat test.txt

执行上述代码后,你将得到以下结果

Line #1 
Line #2 

让我们编写 SED 脚本。

[jerry]$ sed -n '$p' test.txt

执行上述代码后,你将得到以下结果

Line #2 

Dos2unix 命令

在 DOS 环境中,换行符由 CR/LF 字符组合表示。以下“dos2unix”命令的模拟将 DOS 换行符转换为 UNIX 换行符。在 GNU/Linux 中,此字符通常被视为“^M”(控制 M)字符。

[jerry]$ echo -e "Line #1\r\nLine #2\r" > test.txt 
[jerry]$ file test.txt

执行上述代码后,你将得到以下结果

test.txt: ASCII text, with CRLF line terminators 

让我们使用 SED 模拟该命令。

[jerry]$ sed 's/^M$//' test.txt > new.txt   # Press "ctrl+v" followed "ctrl+m" to generate 
"^M" character. 
[jerry]$ file new.txt

执行上述代码后,你将得到以下结果

new.txt: ASCII text 

现在让我们显示文件内容。

[jerry]$ cat -vte new.txt 

执行上述代码后,你将得到以下结果

Line #1$ 
Line #2$

Unix2dos 命令

类似于“dos2unix”,存在“unix2dos”命令,它将 UNIX 换行符转换为 DOS 换行符。以下示例显示了相同的模拟。

[jerry]$ echo -e "Line #1\nLine #2" > test.txt 
[jerry]$ file test.txt 

执行上述代码后,你将得到以下结果

test.txt: ASCII text

让我们使用 SED 模拟该命令。

[jerry]$ sed 's/$/\r/' test.txt  > new.txt 
[jerry]$ file new.txt

执行上述代码后,你将得到以下结果

new.txt: ASCII text, with CRLF line terminators

现在让我们显示文件内容。

Now let us display the file contents.

执行上述代码后,你将得到以下结果

Line #1^M$ 
Line #2^M$ 

Cat -E 命令

"cat -E" 命令在行尾显示美元符号 ($) 字符。以下 SED 示例是对其的模拟。

[jerry]$ echo -e "Line #1\nLine #2" > test.txt 
[jerry]$ cat -E test.txt 

执行上述代码后,你将得到以下结果

Line #1$ 
Line #2$

让我们使用 SED 模拟该命令。

[jerry]$ sed 's|$|&$|' test.txt

执行上述代码后,你将得到以下结果

Line #1$ 
Line #2$

Cat -ET 命令

"cat -ET" 命令在每行末尾显示美元符号 ($) 并将 TAB 字符显示为“^I”。以下示例显示了使用 SED 模拟“cat -ET”命令。

[jerry]$ echo -e "Line #1\tLine #2" > test.txt 
[jerry]$ cat -ET test.txt

执行上述代码后,你将得到以下结果

Line #1^ILine #2$ 

让我们使用 SED 模拟该命令。

[jerry]$ sed -n 'l' test.txt | sed 'y/\\t/^I/'

执行上述代码后,你将得到以下结果

Line #1^ILine #2$ 

Nl 命令

"nl" 命令简单地对文件行进行编号。以下 SED 脚本模拟了此行为。

[jerry]$ echo -e "Line #1\nLine #2" > test.txt 
[jerry]$ sed = test.txt | sed 'N;s/\n/\t/'

执行上述代码后,你将得到以下结果

1 Line #1 
2 Line #2

第一个 SED 表达式打印行号及其内容,第二个 SED 表达式合并这两行并将换行符转换为 TAB 字符。

Cp 命令

"cp" 命令创建文件的另一个副本。以下 SED 脚本模拟了此行为。

[jerry]$ sed -n 'w dup.txt' data.txt 
[jerry]$ diff data.txt dup.txt 
[jerry]$ echo $? 

执行上述代码后,你将得到以下结果

0

Expand 命令

"expand" 命令将 TAB 字符转换为空格。以下代码显示了它的模拟。

[jerry]$ echo -e "One\tTwo\tThree" > test.txt 
[jerry]$ expand test.txt > expand.txt 
[jerry]$ sed 's/\t/     /g' test.txt > new.txt 
[jerry]$ diff new.txt expand.txt  
[jerry]$ echo $? 

执行上述代码后,你将得到以下结果

0 

Tee 命令

"tee" 命令将数据转储到标准输出流以及文件。下面给出了“tee”命令的模拟。

[jerry]$ echo -e "Line #1\nLine #2" | tee test.txt  
Line #1 
Line #2 

让我们使用 SED 模拟该命令。

[jerry]$ sed -n 'p; w new.txt' test.txt  

执行上述代码后,你将得到以下结果

Line #1 
Line #2

Cat -s 命令

UNIX“cat -s”命令抑制重复的空输出行。以下代码显示了“cat -s”命令的模拟。

[jerry]$ echo -e "Line #1\n\n\n\nLine #2\n\n\nLine #3" > test.txt  
[jerry]$ cat -s test.txt 

执行上述代码后,你将得到以下结果

Line #1  
Line #2
Line #3

让我们使用 SED 模拟该命令。

[jerry]$ sed '1s/^$//p;/./,/^$/!d' test.txt 

执行上述代码后,你将得到以下结果

Line #1  
Line #2  
Line #3 

Grep 命令

默认情况下,“grep”命令在模式匹配成功时打印一行。以下代码显示了它的模拟。

[jerry]$ echo -e "Line #1\nLine #2\nLine #3" > test.txt  
[jerry]$ grep "Line #1" test.txt 

执行上述代码后,你将得到以下结果

Line #1

让我们使用 SED 模拟该命令。

[jerry]$ sed -n '/Line #1/p' test.txt 

执行上述代码后,你将得到以下结果

Line #1 

Grep -v 命令

默认情况下,“grep -v”命令在模式匹配失败时打印一行。以下代码显示了它的模拟。

[jerry]$ echo -e "Line #1\nLine #2\nLine #3" > test.txt  
[jerry]$ grep -v "Line #1" test.txt

执行上述代码后,你将得到以下结果

Line #2 
Line #3 

让我们使用 SED 模拟该命令。

[jerry]$ sed -n '/Line #1/!p' test.txt

执行上述代码后,你将得到以下结果

Line #2 
Line #3

Tr 命令

"tr" 命令转换字符。下面给出了它的模拟。

[jerry]$ echo "ABC" | tr "ABC" "abc" 

执行上述代码后,你将得到以下结果

abc

让我们使用 SED 模拟该命令。

[jerry]$ echo "ABC" | sed 'y/ABC/abc/'

执行上述代码后,你将得到以下结果

abc
广告