Sed流编辑器是一款功能强大的编辑工具,能够利用极少输入内容完成多种操作。在上一篇教程里,我们探讨了sed文本编辑器基础知识。
本文将继续介绍其它更为先进的议题。
提供多个编辑序列
有时候,我们可能需要同时向sed发送多条命令。大家可以通过多种方式实现这一效果。
如果需要编辑的文件尚不存在,在这里我们重新创建以完成接下来的操作:
cd
cp /usr/share/common-licenses/BSD .
cp /usr/share/common-licenses/GPL-3 .
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..." > annoying.txt
由于sed通过标准输入与输出进行操作,因此我们需要通过一条管道将不同调用整合起来(请记得注释掉&,因为这意味着使用完全匹配模式):
sed 's/and/\&/' annoying.txt | sed 's/people/horses/'
this 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的内置功能。
我们可以在各命令前使用-e选项对多条命令进行合并:
sed -e 's/and/\&/' -e 's/people/horses/' annoying.txt
另一种命令整合方法是使用分号字符来分隔不同命令,具体方式与上一条基本相同:
sed 's/and/\&/;s/people/horses/' annoying.txt
注意,在使用-e构建命令时,我们需要使用引号对不同命令进行分组。不过在使用引号时,全部命令都可包含在单一引号组之内。
虽然这两种方式都很可行,但有时候我们仍然需要使用前面提到的管道模式以实现某些特定功能。
例如“=”操作符。此操作符会在各现有行之间插入一个数字行,输出结果如下:
sed '=' annoying.txt
1
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' annoying.txt
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...
将这两条命令结合起来,照理说内容行与编号行之间应该会增加一个空行,但实际情况并非如此:
sed '=;G' annoying.txt
1
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 '=' annoying.txt | sed 'G'
1
this is the song that never ends
2
yes, it goes on and on, my friend
3
some people started singing it
. . .
. . .
这次结果就与预期相符了。请注意,部分常见操作需要依此进行,特别是在将多条命令加以结合时,结果往往会与预期不符。
高级寻址
Sed addressable命令的最大优势之一,在于可利用正则表达式作为选择标准。这意味着我们不必受限于之前提到的各类已知行值:
sed '1,3s/.*/Hello/' annoying.txt
Hello
Hello
Hello
not knowing what it was
and they'll continue singing it forever
just because...
相反,我们也可以使用正则表达式匹配包含特定模式的行。我们可以在命令中在两个斜杠(/)内添加匹配模式:
sed '/singing/s/it/& loudly/' annoying.txt
this 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”放在包含有“singing”字符串的各行的第一个“it”之后。注意,第二与第四行并不符合这一模式,因此内容并未变化。
我们还可以进一步提升寻址表达式的复杂性,从而灵活地执行各项命令。
示例本身并不复杂,但我们可以使用正则表达式为其它命令生成地址。以下命令中,其匹配任意空行(即行的开头与结尾紧密连接),而后将空行删除:
sed '/^$/d' GPL-3
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”的行之间的各行:
sed '/^START$/,/^END$/d' inputfile
这里需要强调的是,此操作将删除第一条“START”与第一条“END”间的全部内容,而后在遇到下一个“START”标记后再执行一次。
如果我们需要反转地址(操作任何不匹配模式的行),则可在模式后添加感叹号。
例如,我们可以删除一切非空行(没什么用,仅作为示例):
sed '/^$/!d' GPL-3
使用“保持缓存(Hold Buffer)”
保持缓存,即hold b uffer,能够切实提升sed的多行编辑能力。保持缓存是一种临时存储区,可通过特定命令进行修改。
利用这种额外的缓冲区,我们可以在操作某些行的时候将其它行存储在其中,而后在必要时使用这些缓冲内容。
以下为保持缓存中的各命令及作用:
h: 将当前模式缓冲区(即我们当前匹配并在处理的行)复制到保持缓存内(这会覆盖缓冲区的原有内容)。
H: 将现有模式缓冲区附加到当前保持缓存末尾,以新行符(\n)分隔。
g: 将现有保持缓存复制到当前模式缓存当中。这会覆盖掉原有模式缓存。
G: 将当前保持模式附加到当前模式缓存末尾,以新行符(\n)分隔。
x: 交换当前模式与保持缓存。
保持缓存中的内容无法直接操作,直到其被转移至模式缓存。
下面来看看比较复杂的示例。
以下示例为如何合并相邻行(sed提供内置命令执行此类任务。其中N命令会将下一行合并至当前行中):
sed -n '1~2h;2~2{H;g;s/\n/ /;p}' annoying.txt
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...
下面来看命令的具体内容。
首先是“-n”选项,其用于禁用自动输出。如此一来,sed只会输出我们要求其输出的内容。
脚本的第一部分为“1~2h”。开头为地址指定,代表着在第一行执行序列操作,而后每隔一行进行操作(即各奇数行)。“h”部分要求该命令将匹配的行复制到保持缓存当中。
命令的下半部分较为复杂。其同样以地址指定开头,这一次引用的是编号行(与第一条命令不同)。
命令的其余部分放置在括号当中。意味着命令其余部分将继承之前指定的地址。如果没有括号,则只有“H”命令会继承该地址,而其余部分则在每行上执行。
“H”命令会复制一个新行符,其后为当前模式缓存,最后为当前保持缓存。
此保持模式(一个奇数行,随后是一个新行符,最后是编号行)随后通过“g”命令被复制回模式缓存(覆盖此前的模式缓存)。
接下来,该新行符被替换为空格,该行则以“p”命令进行输出。
如果使用“N”命令,那么如上所述,命令内容会大大缩短而结果不变:
sed -n 'N;s/\n/ /p' annoying.txt
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...
使用Sed脚本
在使用更为复杂的命令之前,我们可以先在文本编辑器中进行命令汇总。通过这种方式,我们能够针对单一目标执行大量命令操作。
例如,如果大家希望撰写纯文本信息,但又需要在使用文本前将其整理为某种标准化格式,则可使用sed脚本来简化整个过程。
无需逐个输入命令,我们可以将各命令纳入一套脚本,并将其作为参数提交给sed。一套sed脚本就相当于一份原始sed命令列表(通常包含在单引号之内)。
例如:
s/this/that/g
s/snow/rain/g
1,5s/pinecone/apricot/g
而后我们可以使用以下语法调用该文件:
sed -f sedScriptName fileToEdit
如此一来,我们就能够编辑文件并借此创建任意需要的文本格式了。
总结
在本教程中,我们更为深入地对sed进行了论述。
Sed的命令在刚接触时显得难以理解,而且通常需要不断实验才能掌握其用法。因此,请大家多加练习以巩固体会。
不过相信大家也已经从示例中感受到了sed的强大能力,希望睿智的你能够进一步发挥想象力,凭借这款出色的工具更好地完成日常任务。