介绍
该sed
流编辑器是一个功能强大的编辑工具,可以使用很少的投入巨大变化。在上一篇文章中sed
,您探索了使用 sed 编辑文本的基础知识。
本文将通过检查一些更高级的主题来继续介绍。
注意:本教程使用sed
在 Ubuntu 和其他 Linux 操作系统上找到的 GNU 版本。如果您使用的是 macOS,您将拥有具有不同选项和参数的 BSD 版本。您可以安装的GNU版本,sed
与家酿使用brew install gnu-sed
。
要完成本教程,您需要一些文件进行操作,您应该从第一个教程中获得这些文件。如果您没有它们,可以使用以下命令重新创建它们:
- cd
- cp /usr/share/common-licenses/BSD .
- echo "this is the song that never ends
- yes, it goes on and on, my friend
- some people started singing it
- not knowing what it was
- and they'll continue singing it forever
- just because..." > song.txt
此外,您将在本教程中使用GPL 3 许可证,因此也复制该文件:
- cp /usr/share/common-licenses/GPL-3 .
如果你没有它,你可以下载它curl
:
- curl -o GPL-3 https://www.gnu.org/licenses/gpl-3.0.txt
现在您有了这些文件,您将探索使用sed
多个命令。
提供多个编辑序列
在很多情况下,您可能希望同时传递多个命令sed
。有几种方法可以实现这一点。
由于sed
通过标准输入和输出操作,您可以sed
通过管道将不同的调用串在一起。执行此命令将单词替换为and
apersand ( &
),并将单词people
替换为horses
:
- sed 's/and/\&/' song.txt | sed 's/people/horses/'
请注意,您需要将“&”转义,因为它意味着“完整匹配的模式”到sed
):
您将看到以下输出:
Outputthis is the song that never ends
yes, it goes on & on, my friend
some horses started singing it
not knowing what it was
& they'll continue singing it forever
just because...
这有效,但它会因多次调用 造成不必要的开销sed
,需要更多的输入,并且没有利用sed
的内置功能。
sed
通过-e
在每个命令之前使用选项,您可以将各种命令串成字符串。这是您将如何重写之前的命令:
- sed -e 's/and/\&/' -e 's/people/horses/' song.txt
将命令串在一起的另一种方法是使用分号字符 ( ;
) 来分隔不同的命令。这与前面的示例相同,但-e
不需要“ ”:
- sed 's/and/\&/;s/people/horses/' song.txt
请注意,在使用-e
构造时,您需要为不同的命令使用单独的单引号组。但是,当用分号分隔命令时,所有命令都放在一个单引号命令字符串中。尽管这两种表达多个命令的方式很有用,但有时仍需要使用先前的管道技术。
考虑=
运营商。此运算符在每个现有行之间的新行上插入行号。输出如下所示:
- sed '=' song.txt
这是您将看到的输出:
Output1
this is the song that never ends
2
yes, it goes on and on, my friend
3
some people started singing it
4
not knowing what it was
5
and they'll continue singing it forever
6
just because...
但是,如果您想通过修改文本来更改编号的格式,您会发现事情没有按预期工作。
为了演示,让我们看一下G
命令,默认情况下,它在每行之间输入一个空行(这实际上更复杂,但您将在稍后探索):
- sed 'G' song.txt
结果如下:
Outputthis is the song that never ends
yes, it goes on and on, my friend
some people started singing it
not knowing what it was
and they'll continue singing it forever
just because...
如果将这两个命令结合起来,您可能希望在每个
常规行和行号行之间有一个空格:
- sed '=;G' song.txt
但是,您会得到不同的东西:
Output1
this is the song that never ends
2
yes, it goes on and on, my friend
3
some people started singing it
4
not knowing what it was
. . .
. . .
发生这种情况是因为=
操作员直接修改了实际的输出流。这意味着您无法使用结果进行更多编辑。
您可以通过使用两个sed
调用来解决这个问题,将第一个sed
修改视为第二个的简单文本流:
- sed '=' song.txt | sed 'G'
您现在可以看到预期的结果:
Output1
this is the song that never ends
2
yes, it goes on and on, my friend
3
some people started singing it
. . .
. . .
请记住,某些命令的操作方式是这样的,尤其是当您将多个命令串在一起并且输出与您预期的不同时。
高级寻址
sed
的可寻址命令的优点之一是正则表达式可以用作选择标准。这意味着您不仅限于对已知行值进行操作,就像您之前看到的那样:
- sed '1,3s/.*/Hello/' song.txt
OutputHello
Hello
Hello
not knowing what it was
and they'll continue singing it forever
just because...
相反,您可以使用正则表达式仅匹配包含特定模式的行。为此,在给出命令字符串之前,将匹配模式放在两个正斜杠 (/) 之间:
- sed '/singing/s/it/& loudly/' song.txt
Outputthis is the song that never ends
yes, it goes on and on, my friend
some people started singing it loudly
not knowing what it was
and they'll continue singing it loudly forever
just because...
在此示例中,您在包含字符串 的每一行中loudly
第一次出现 之后放置it
了singing
。请注意,第二行和第四行未更改,因为它们与模式不匹配。
寻址表达式可以是任意复杂的。这为执行命令提供了很大的灵活性。
这不是一个复杂的示例,但它演示了使用正则表达式为其他命令生成地址。下面的命令匹配任何空行(一行的开头,紧接着是行的结尾)并将它们传递给 delete 命令:
- sed '/^$/d' GPL-3
这是您将看到的输出:
Output GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
. . .
. . .
请记住,您也可以在范围的任一侧使用正则表达式。例如,您可以删除从仅包含单词的行开始的行,START
直到读到的行END
,
例如,创建一个名为 的文件inputfile
:
- echo "This is an input file
- START
- this is the text we don't want
- END
- This is additional text" > inputfile
现在使用sed
删除的内容START
和END
:
- sed '/^START$/,/^END$/d' inputfile
您将看到以下输出:
This is an input file
This is additional text
不过需要注意的一点是,这将删除从 firstSTART
到 first 的所有内容END
,然后如果遇到另一个START
标记,则重新开始删除。
如果要反转地址(在不匹配模式的任何行上操作),可以在模式后使用感叹号 ( !
)。
例如,您可以使用以下命令删除任何非空白行(不是非常有用,只是一个示例):
- sed '/^$/!d' GPL-3
这会导致大量空白输出,因为sed
默认情况下仍然打印行:
Output
地址不需要是要反转的复杂表达式。反转对常规编号地址的工作方式也相同。
使用保持缓冲器
增加sed
执行多行感知编辑能力的一项功能就是所谓的“保持缓冲区”。保持缓冲区是一个可以被某些命令修改的临时存储区域。
这个额外缓冲区的存在意味着您可以在处理其他行的同时存储行,然后根据需要对每个缓冲区进行操作。
以下是影响保持缓冲区的命令:
- h:将当前模式缓冲区(您当前匹配并正在处理的行)复制到保持缓冲区(这会擦除保持缓冲区的先前内容)。
- H:将当前模式缓冲区附加到当前保持模式的末尾,由换行符 (\n) 分隔。
- g:将当前保持缓冲区复制到当前模式缓冲区中。先前的模式缓冲区被擦除。
- G:将当前保持模式附加到当前模式缓冲区的末尾,由换行符 (\n) 分隔。
- x:交换当前模式和保持缓冲区。
在以一种或另一种方式将其移动到模式缓冲区之前,无法操作保持缓冲区的内容。
让我们用一个复杂的例子来探讨这个想法。
这是一个如何连接相邻行的程序示例(sed
实际上有一个内置命令可以为我们处理很多事情。该N
命令将下一行附加到当前行。你将做一些困难的事情虽然是为了练习):
- sed -n '1~2h;2~2{H;g;s/\n/ /;p}' song.txt
这是您将看到的输出:
Outputthis is the song that never ends yes, it goes on and on, my friend
some people started singing it not knowing what it was
and they'll continue singing it forever just because...
这需要消化很多,所以让我们分解一下。
首先要注意的是该-n
选项用于抑制自动打印。sed
只有当你特别告诉它时才会打印。
指令的第一部分是1\~2h
. 开头是地址规范,意思是在第一行执行后续操作,然后每隔一行(每个奇数行)。该h
部分是将匹配行复制到保持缓冲区的命令。
命令的后半部分更为复杂。同样,它以地址规范开始。这一次,它指的是偶数行(与第一个命令相反)。
命令的其余部分用大括号括起来。这意味着其余的命令将继承刚刚指定的地址。没有大括号,只有“H”命令会继承地址,其余的命令将在每一行执行。
该H
命令复制一个换行符,后跟当前模式缓冲区,复制到当前保持模式的末尾。
这个保持模式(奇数行,后跟换行符,偶数行)然后用命令复制回模式缓冲区(替换之前的模式缓冲区)g
。
接下来,换行符被替换为空格,并用p
命令打印该行。
如果您好奇,使用该N
命令会大大缩短此过程。以下命令将产生与您刚刚看到的相同的结果:
- sed -n 'N;s/\n/ /p' song.txt
使用脚本
当您开始使用更复杂的命令时,在文本编辑器中编写它们可能会有所帮助。如果您想将大量命令应用于单个目标,这也很有帮助。
例如,如果您喜欢以纯文本形式撰写消息,但在使用文本之前需要执行一组标准化格式,那么sed
脚本将很有用。
相反,打字每一套的sed
电话,你可以把命令脚本并提供其作为一个参数sed
。甲sed
脚本仅仅是原始的列表sed
的命令(通常是单引号字符之间的部分)。
要尝试此操作,请创建一个sed_script
包含以下内容的新文件:
- echo "s/this/that/g
- s/people/horses/g
- 1,5s/it/that/g" > sed_script
保存文件并退出编辑器。
现在告诉sed
使用-f
开关使用该文件:
- sed -f sed_script song.txt
结果将如下所示:
Outputthat is the song that never ends
yes, that goes on and on, my friend
some horses started singing that
not knowing what that was
and they'll continue singing that forever
just because...
这允许您将所有编辑放在一个文件中,并在需要符合您创建的格式的任意文本文件上执行它。
结论
Sed 的命令一开始并不总是很容易理解,通常需要进行一些实际实验才能了解它们的实用性。因此,建议您在实际需要之前练习操作文本。牢记最终目标并尝试仅使用sed
.
希望在这一点上,您已经开始了解正确掌握sed
可以给您带来的力量。你越舒服sed
,从长远来看,你需要做的工作就越少。