很少有 Unix 命令像 sed、grep 和 awk 这样出名。它们经常被归为一类,可能是因为它们有奇怪的名字和强大的文本解析工具。它们也共享一些句法和逻辑上的相似之处。虽然它们都可用于解析文本,但每个都有其专长。本文研究 sed
命令,它是一个流编辑器。
我之前写过关于 sed 及其远亲 ed 的文章。为了熟悉 sed,对 ed 有一定的了解会有所帮助,因为这有助于你习惯缓冲区的概念。本文假设你熟悉 sed 的基本知识,这意味着你至少运行过经典的 s/foo/bar/
风格的查找和替换命令。
[下载我们的免费 sed 速查表]
安装 sed
如果你正在使用 Linux、BSD 或 macOS,你已经安装了 GNU 或 BSD sed。这些是对原始 sed
命令的独特重新实现,虽然它们很相似,但也存在细微差异。本文已在 Linux 和 NetBSD 版本上进行了测试,因此在这种情况下,你可以使用计算机上的任何 sed,但对于 BSD sed,你必须仅使用短选项(例如 -n
而不是 --quiet
)。
GNU sed 通常被认为是功能最丰富的 sed,因此无论你是否运行 Linux,你都可能想尝试一下。如果你在端口树中找不到 GNU sed(在非 Linux 系统上通常称为 gsed),那么你可以从 GNU 网站 下载其源代码。安装 GNU sed 的好处是,你可以使用它的额外功能,但也可以约束它以符合 POSIX sed 规范,如果你需要可移植性的话。
MacOS 用户可以在 MacPorts 或 Homebrew 上找到 GNU sed。
在 Windows 上,你可以使用 Chocolatey 安装 GNU sed。
理解模式空间和保持空间
Sed 一次只处理一行。因为它没有可视化显示,所以它创建了一个模式空间,这是内存中的一个空间,包含来自输入流的当前行(删除了任何尾随换行符)。一旦你填充了模式空间,sed 就会执行你的指令。当它到达命令末尾时,sed 会将模式空间的内容打印到输出流。默认输出流是 stdout,但输出可以使用 --in-place=.bak
选项重定向到文件,甚至重定向回同一文件。
然后循环再次从下一输入行开始。
为了在你使用 sed 浏览文件时提供一点灵活性,sed 还提供了一个保持空间(有时也称为保持缓冲区),这是 sed 内存中保留用于临时数据存储的空间。你可以将保持空间视为剪贴板,事实上,这正是本文演示的内容:如何使用 sed 复制/剪切和粘贴。
首先,创建一个示例文本文件,内容如下
Line one
Line three
Line two
将数据复制到保持空间
要将内容放入 sed 的保持空间,请使用 h
或 H
命令。小写 h
告诉 sed 覆盖保持空间的当前内容,而大写 H
告诉它将数据追加到保持空间中已有的内容。
单独使用时,没有什么可看的
$ sed --quiet -e '/three/ h' example.txt
$
--quiet
选项(简写为 -n
)禁止所有输出,但 sed 为我的搜索要求执行的操作除外。在本例中,sed 选择任何包含字符串 three
的行,并将其复制到保持空间。我没有告诉 sed 打印任何内容,因此不产生输出。
从保持空间复制数据
为了深入了解保持空间,你可以从保持空间复制其内容,并使用 g
命令将其放入模式空间。观察会发生什么
$ sed -n -e '/three/h' -e 'g;p' example.txt
Line three
Line three
第一个空行打印是因为当保持空间第一次复制到模式空间时,它是空的。
接下来的两行包含 Line three
,因为这是从第二行开始保持空间中的内容。
此命令使用两个唯一的脚本 (-e
) 纯粹是为了帮助提高可读性和组织性。将步骤划分为单独的脚本可能很有用,但从技术上讲,此命令与一个脚本语句的效果相同
$ sed -n -e '/three/h ; g ; p' example.txt
Line three
Line three
将数据追加到模式空间
G
命令将换行符和保持空间的内容追加到模式空间。
$ sed -n -e '/three/h' -e 'G;p' example.txt
Line one
Line three
Line three
Line two
Line three
此输出的前两行包含模式空间 (Line one
) 和空保持空间的内容。接下来的两行与搜索文本 (three
) 匹配,因此它包含模式空间和保持空间。保持空间对于第三对行没有变化,因此模式空间 (Line two
) 与保持空间(仍然是 Line three
)一起打印在末尾。
使用 sed 进行剪切和粘贴
现在你已经知道如何将字符串从模式空间来回移动到保持空间,你可以设计一个 sed 脚本,该脚本可以在文档中复制、然后删除、然后粘贴一行。例如,本文的示例文件中的 Line three
顺序错误。Sed 可以修复它
$ sed -n -e '/three/ h' -e '/three/ d' \
-e '/two/ G;p' example.txt
Line one
Line two
Line three
- 第一个脚本查找包含字符串
three
的行,并将其从模式空间复制到保持空间,替换当前保持空间中的任何内容。 - 第二个脚本删除任何包含字符串
three
的行。这完成了相当于文字处理器或文本编辑器中的剪切操作。 - 最后一个脚本查找包含
two
的行,并将保持空间的内容追加到模式空间,然后打印模式空间。
任务完成。
使用 sed 编写脚本
再次强调,使用单独的脚本语句纯粹是为了视觉和精神上的简洁。剪切和粘贴命令可以作为一个脚本工作
$ sed -n -e '/three/ h ; /three/ d ; /two/ G ; p' example.txt
Line one
Line two
Line three
它甚至可以编写为专用脚本文件
#!/usr/bin/sed -nf
/three/h
/three/d
/two/ G
p
要运行脚本,请将其标记为可执行文件并在你的示例文件上尝试它
$ chmod +x myscript.sed
$ ./myscript.sed example.txt
Line one
Line two
Line three
当然,你需要解析的文本越可预测,就越容易使用 sed 解决你的问题。为 sed 操作(例如复制和粘贴)发明“配方”通常是不切实际的,因为触发操作的条件可能因文件而异。但是,你越流利地使用 sed 命令,就越容易根据你需要解析的输入来设计复杂的操作。
重要的是识别不同的操作,了解 sed 何时移动到下一行,以及预测模式空间和保持空间可能包含的内容。
下载速查表
Sed 很复杂。它只有十几个命令,但其灵活的语法和原始功能意味着它充满了无限的潜力。我过去常常参考几页巧妙的单行代码,试图充分利用 sed,但直到我开始发明(有时是重新发明)自己的解决方案,我才觉得自己开始真正学习 sed。如果你正在寻找关于命令的温和提醒和关于语法的有用提示,下载我们的 sed 速查表,并一劳永逸地开始学习 sed!
评论已关闭。