一、sed编辑器简介
sed编辑器被称作流编辑器(stream editor),和普通的交互式文本编辑器恰好相反。在交互式文本编辑器中(比如vim),你可以用键盘命令来交互式地插入、删除或替换数据中的文本。流编辑器则会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流。
sed编辑器可以根据命令来处理数据流中的数据,这些命令要么从命令行中输入,要么存储在一个命令文本文件中。sed编辑器会执行下列操作:
- (1) 一次从输入中读取一行数据。
- (2) 根据所提供的编辑器命令匹配数据。
- (3) 按照命令修改流中的数据。
- (4) 将新的数据输出到 STDOUT 。
二、单行数据处理命令
在流编辑器将所有命令与一行数据匹配完毕后,它会读取下一行数据并重复这个过程。在流编辑器处理完流中的所有数据行后,它就会终止。由于命令是按顺序逐行给出的,sed编辑器只需对数据流进行一遍处理就可以完成编辑操作。这使得sed编辑器要比交互式编辑器快得多,你可以快速完成对数据的自动修改。
sed 命令的格式如下:
sed options script file
- -e script
在处理输入时,将 script 中指定的命令添加到已有的命令中 - -f file
在处理输入时,将 file 中指定的命令添加到已有的命令中 - -n
不产生命令输出,使用 print 命令来完成输出
1 s
替换命令
1.1 s
命令(substitute)
- 从echo命令接受数据流
$ echo "I am good" | sed 's/good/best/'
- 从文件中接受数据流
$ cat test1.txt
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
$ sed 's/dog/cat/' test1.txt
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
- 在命令行中使用多个编辑器命令
要在 sed 命令行上执行多个命令时,只要用 -e 选项就可以了。
$ sed -e 's/brown/green/; s/dog/cat/' test1.txt
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
两个命令都作用到文件中的每行数据上。命令之间必须用分号隔开,并且在命令末尾和分号之间不能有空格。
如果不想用分号,也可以用bash shell中的次提示符>
来分隔命令。只要输入第一个单引号标示出sed程序脚本的起始(sed编辑器命令列表),bash会继续提示你输入更多命令,直到输入了标示结束的单引号。
$ sed -e '
> s/brown/green/
> s/fox/elephant/
> s/dog/cat/' data1.txt
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
- 从文件中读取编辑器命令
如果有大量要处理的 sed 命令,那么将它们放进一个单独的文件中通常会更方便一些。可以在 sed 命令中用 -f 选项来指定文件。
$ cat script1.sed
s/brown/green/
s/fox/elephant/
s/dog/cat/
$ sed -f script1.sed data1.txt
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
在这种情况下,不用在每条命令后面放一个分号。sed编辑器知道每行都是一条单独的命令。
跟在命令行输入命令一样,sed编辑器会从指定文件中读取命令,并将它们应用到数据文件中的
每一行上。
$ sed -f script1.sed data1.txt
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
$
在这种情况下,不用在每条命令后面放一个分号。sed编辑器知道每行都是一条单独的命令。
跟在命令行输入命令一样,sed编辑器会从指定文件中读取命令,并将它们应用到数据文件中的
每一行上。
1.2 替换标记
替换命令在替换多行中的文本时能正常工作,但默认情况下它只替换每行中出现的第一处。要让替换命令能够替换一行中不同地方出现的文本必须使用替换标记(substitution flag)。替换标记会在替换命令字符串之后设置。
有4种可用的替换标记:
-
数字
,表明新文本将替换第几处模式匹配的地方
# 把每行的第三个cat替换为dog
$ cat test2.txt
cat cat cat cat cat
cat cat cat cat cat
cat cat cat cat cat
$ sed 's/cat/dog/3' test2.txt
cat cat dog cat cat
cat cat dog cat cat
cat cat dog cat cat
-
g
,表明新文本将会替换所有匹配的文本
# 把所有的cat都替换为dog
$ cat test2.txt
cat cat cat cat cat
cat cat cat cat cat
cat cat cat cat cat
$ sed 's/cat/dog/3' test2.txt
dog dog dog dog dog
dog dog dog dog dog
dog dog dog dog dog
-
p
,表明把匹配的内容要打印出来;
在第一类替换中,可以指定sed编辑器用新文本替换第几处模式匹配的地方,通常会和 sed 的 -n 选项一起使用。
$ cat test3.txt
Line1: I am a cat
Line2: I am a dog
$ sed -n 's/dog/pig/p' test3.txt
Line2: I am a pig
-
w file
,将替换的结果写到文件中。会产生同样的输出,但输出同时被保存文件里
$ sed 's/dog/cat/w test1.o.txt' test1.txt
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
$ cat test1.o.txt
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
通过输出重定向也可以达到这个目的,但不会在终端显示。
$ sed 's/dog/cat/' test1.txt > test1.o2.txt
$ cat test1.o1.txt
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
只有那些包含匹配模式的行才会保存在指定的输出文件中。
1.3 替换字符
有时你会在文本字符串中遇到一些不太方便在替换模式中使用的字符。Linux中一个常见的例子就是正斜线(/)。替换文件中的路径名会比较麻烦。比如,如果想用C shell替换/etc/passwd文件中的bash shell,必须这么做:
sed 's/\/bin\/bash/\/bin\/csh/' /etc/passwd
由于正斜线通常用作字符串分隔符,因而如果它出现在了模式文本中的话,必须用反斜线来转义。这通常会带来一些困惑和错误。
要解决这个问题,sed编辑器允许选择其他字符来作为替换命令中的字符串分隔符:
$ sed 's!/bin/bash!/bin/csh!' /etc/passwd
在这个例子中,感叹号被用作字符串分隔符,这样路径名就更容易阅读和理解了。
2 使用地址
默认情况下,在sed编辑器中使用的命令会作用于文本数据的所有行。如果只想将命令作用于特定行或某些行,则必须用行寻址(line addressing)。
在sed编辑器中有两种形式的行寻址:
以数字形式表示行区间
用文本模式来过滤出行
2.1 数字行寻址
当使用数字方式的行寻址时,可以用行在文本流中的行位置来引用。sed编辑器会将文本流中的第一行编号为1,然后继续按顺序为接下来的行分配行号。在命令中指定的地址可以是单个行号,或是用起始行号、逗号以及结尾行号指定的一定区间范围内的行。
# 将第三行的的第二个cat替换为dog
sed '3s/cat/dog/2' test2.txt
cat cat cat cat cat
cat cat cat cat cat
cat dog cat cat cat
cat cat cat cat cat
cat cat cat cat cat
# 将第三行到第四行的的第二个cat替换为dog
sed '3,4s/cat/dog/2' test2.txt
cat cat cat cat cat
cat cat cat cat cat
cat dog cat cat cat
cat dog cat cat cat
cat cat cat cat cat
# 将第三行及往后的的第二个cat替换为dog
sed '3,$s/cat/dog/2' test2.txt
cat cat cat cat cat
cat cat cat cat cat
cat dog cat cat cat
cat dog cat cat cat
cat dog cat cat cat
2.2 文本行寻址
另一种限制命令作用到哪些行上的方法会稍稍复杂一些。sed编辑器允许指定文本模式来过滤出命令要作用的行,必须用正斜线将要指定的 pattern 封起来。sed编辑器会将该命令作用到包含指定文本模式的行上。
$ cat test4.txt
A has a apple
B has a apple
C has a apple
D has a apple
$ sed '/C/s/apple/orange/' test4.txt
A has a apple
B has a apple
C has a orange
D has a apple
该命令只作用到匹配文本模式的行上。虽然使用固定文本模式能帮你过滤出特定的值,但其作用难免有限。sed编辑器在文本模式中采用了一种称为正则表达式(regular expression)的特性来帮助你创建匹配效果更好的模式。正则表达式允许创建高级文本模式匹配表达式来匹配各种数据。这些表达式结合了一系列通配符、特殊字符以及固定文本字符来生成能够匹配几乎任何形式文本的简练模式。
2.3 命令组合
如果需要在单行上执行多条命令,可以用花括号将多条命令组合在一起。sed编辑器会处理地址行处列出的每条命令。
$ sed '4{
s/D/E/
s/apple/banana/}' test4.txt
A has a apple
B has a apple
C has a apple
E has a banana
两条命令都会作用到该地址上。当然,也可以在一组命令前指定一个地址区间。
3 d
删除命令
$ sed '2d' test4.txt
A has a apple
C has a apple
D has a apple
$ sed '2,3d' test4.txt
A has a apple
D has a apple
$ sed '2,$d' test4.txt
A has a apple
sed '/B/,/C/d' test4.txt
A has a apple
D has a apple
4 i
插入&a
追加命令
如你所期望的,跟其他编辑器类似,sed编辑器允许向数据流插入和附加文本行。
- 插入( insert )命令( i )会在指定行前增加一个新行;
- 附加( append )命令( a )会在指定行后增加一个新行。
# 使用命令行时两个反斜杠不加也可以,只是看起来更美观
$ sed '4i\E has a apple\' test4.txt
A has a apple
B has a apple
C has a apple
E has a apple
D has a apple
$ sed '4a\E has a apple\' test4.txt
A has a apple
B has a apple
C has a apple
D has a apple
E has a apple
# 使用次提示符时非结束行的末尾则必须要加反斜杠
$ sed '4a\
> E has a apple\
> F has a apple' test4.txt
A has a apple
B has a apple
C has a apple
D has a apple
E has a apple
F has a apple
# 在末尾追加行
$ sed '$a\Last one has nothing\' test4.txt
A has a apple
B has a apple
C has a apple
D has a apple
Last one has nothing
5 c
修改命令
修改( change )命令允许修改数据流中整行文本的内容。它跟插入和附加命令的工作机制一样,你必须在 sed 命令中单独指定新行。
# 数字行寻址
$ sed '4c\D has nothing' test4.txt
A has a apple
B has a apple
C has a apple
D has nothing
# 文本行寻址
$ sed '/D/c\D has nothing\' test4.txt
A has a apple
B has a apple
C has a apple
D has nothing
6 y
转换命令
转换( transform )命令( y )是唯一可以处理单个字符的sed编辑器命令。转换命令格式如下。
[address]y/inchars/outchars/ file
转换命令会对 inchars 和 outchars 值进行一对一的映射。 inchars 中的第一个字符会被转换为 outchars 中的第一个字符,第二个字符会被转换成 outchars 中的第二个字符。这个映射过程会一直持续到处理完指定字符。如果 inchars 和 outchars 的长度不同,则sed编辑器会产生一条错误消息。
$ sed 'y/ABCD/EFGH/' test4.txt
E has a apple
F has a apple
G has a apple
H has a apple
转换命令是一个全局命令,也就是说,它会文本行中找到的所有指定字符自动进行转换,而不会考虑它们出现的位置。
7 打印
除了用 p 标记和替换命令显示sed编辑器修改过的行,另外有3个命令也能用来打印数据流中的信息:
-
p
命令用来打印文本行; -
=
命令用来打印行号; -
l
(小写的L)命令用来列出行。
7.1 p
命令打印行
打印命令最常见的用法是打印包含匹配文本模式的行。
$ cat data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
$ sed -n '/number 3/p' data6.txt
This is line number 3.
# 在命令行上用 -n 选项,你可以禁止输出其他行,只打印包含匹配文本模式的行。
# 也可以用它来快速打印数据流中的某些行。
$ sed -n '2,3p' data6.txt
This is line number 2.
This is line number 3.
如果需要在修改之前查看行,也可以使用打印命令,比如与替换或修改命令一起使用。可以创建一个脚本在修改行之前显示该行。
$ sed -n '/3/{
> p
> s/line/test/p
> }' data6.txt
This is line number 3.
This is test number 3.
sed编辑器命令会查找包含数字3的行,然后执行两条命令。首先,脚本用 p
命令来打印出原始行;然后它用 s
命令替换文本,并用 p
标记打印出替换结果。输出同时显示了原来的行文本和新的行文本。
7.2 =
命令打印行号
sed编辑器在实际的文本行出现前打印了行号。如果你要在数据流中查找特定文本模式的话,
等号命令用起来非常方便。
$ sed -n '/number 4/{
> =
> p
> }' data6.txt
> 4
> This is line number 4.
利用 -n 选项,你就能让sed编辑器只显示包含匹配文本模式的行的行号和文本。
7.3 l
命令列出行
列出( list )命令( l )可以打印数据流中的文本和不可打印的ASCII字符。任何不可打印字符要么在其八进制值前加一个反斜线,要么使用标准C风格的命名法(用于常见的不可打印字符),比如 \t ,来代表制表符。
$ cat data9.txt
This line contains tabs.
$ sed -n 'l' data9.txt
This\tline\tcontains\ttabs.$
8 使用 sed 处理文件
替换命令包含一些可以用于文件的标记。还有一些sed编辑器命令也可以实现同样的目标,不需要非得替换文本。
8.1 写入文件
w
命令用来向文件写入行。该命令的格式如下:
[address]sed w filename
下面的例子是将数据流中的前两行打印到一个文本文件中。
$ sed -n '1,2w test.txt' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
$ cat test.txt
This is line number 1.
This is line number 2.
如果要根据一些公用的文本值从主文件中创建一份数据文件,比如下面的邮件列表中的,那么 w 命令会非常好用。
$ cat data11.txt
Blum, R Browncoat
McGuiness, A Alliance
Bresnahan, C Browncoat
Harken, C Alliance
$ sed -n '/Browncoat/w Browncoats.txt' data11.txt
$ cat Browncoats.txt
Blum, R Browncoat
Bresnahan, C Browncoat
sed编辑器会只将包含文本模式的数据行写入目标文件。
8.2 从文件读取数据
读取( read )命令( r )允许你将一个独立文件中的数据插入到数据流中。读取命令的格式如下:
[address]r filename
filename 参数指定了数据文件的绝对路径或相对路径。你在读取命令中使用地址区间,只能指定单独一个行号或文本模式地址。sed编辑器会将文件中的文本插入到指定地址后。
$ cat data12.txt
This is an added line.
This is the second added line.
$ sed '3r data12.txt' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is an added line.
This is the second added line.
This is line number 4.
sed编辑器会将数据文件中的所有文本行都插入到数据流中。同样的方法在使用文本模式地址时也适用。
$ sed '/number 2/r data12.txt' data6.txt
This is line number 1.
This is line number 2.
This is an added line.
This is the second added line.
This is line number 3.
This is line number 4.
$
如果你要在数据流的末尾添加文本,只需用美元符地址符就行了。
$ sed '$r data12.txt' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
This is an added line.
This is the second added line.
读取命令的另一个很酷的用法是和删除命令配合使用:利用另一个文件中的数据来替换文件中的占位文本。举例来说,假定你有一份套用信件保存在文本文件中:
$ cat notice.std
Would the following people:
LIST
please report to the ship's captain.
$ cat data11.txt
Blum, R Browncoat
McGuiness, A Alliance
Bresnahan, C Browncoat
Harken, C Alliance
套用信件将通用占位文本 LIST 放在人物名单的位置。要在占位文本后插入名单,只需读取命令就行了。但这样的话,占位文本仍然会留在输出中。要删除占位文本的话,你可以用删除命令。结果如下:
$ sed '/LIST/{
r data11.txt
> d
> }' notice.std
Would the following people:
Blum, R Browncoat
McGuiness, A Alliance
Bresnahan, C Browncoat
Harken, C Alliance
please report to the ship's captain.
首先使用文本寻址,在占位文本LIST之后追加data11.txt的内容,之后d
命令删除占位文本LIST,现在占位文本已经被替换成了数据文件中的名单。
三、多行数据处理命令
有时,我们并不是仅仅对每一行单独地进行操作,而是进行多行操作。比如查找一串字符,但这个字符可能分布在两行甚至多行;再比如,你要根据某一行的特征对相邻的几行进行操作,等等。这时你就要用到多行操作命令了。
sed编辑器包含了三个可用来处理多行文本的特殊命令。
- N :将数据流中的下一行加进来创建一个多行组(multiline group)来处理。
- D :删除多行组中的一行。
- P :打印多行组中的一行。
后面几节将会进一步讲解这些多行命令并向你演示如何在脚本中使用它们。
1 next
命令
在讲解多行 next 命令之前,首先需要看一下单行版本的 next 命令是如何工作的,然后就比较容易理解多行版本的 next 命令是如何操作的了。
1.1 单行的 next
命令
n
命令可实现对匹配行的下一行文本进行处理,而不用重新回到命令的最开始再执行一遍。
在这个例子中,你有个数据文件,共有5行内容,其中的两行是空的。目标是删除首行之后的一行空白行,而留下其他的空白行。如果写一个删掉空白行的sed脚本,你会删掉两个空白行。
$ cat data1.txt
This is the header line.
This is a data line.
This is the last line.
$ sed '/^$/d' data1.txt
This is the header line.
This is a data line.
This is the last line.
由于要删除的行是空行,没有任何能够标示这种行的文本可供查找。解决办法是用 n 命令。在这个例子中,脚本要查找含有单词header的那一行。找到之后, n 命令会让sed编辑器移动到文本的下一行,也就是那个空行。
$ sed '/header/{n ; d}' data1.txt
This is the header line.
This is a data line.
This is the last line.
这时,sed编辑器会继续执行命令列表,该命令列表使用 d 命令来删除空白行。sed编辑器执行完命令脚本后,会从数据流中读取下一行文本,并从头开始执行命令脚本。因为sed编辑器再也找不到包含单词header的行了。所以也不会有其他行会被删掉。
1.2 合并文本行
了解了单行版的 next 命令,现在来看看多行版的。单行 next 命令会将数据流中的下一文本行移动到sed编辑器的工作空间(称为模式空间)。多行版本的 next 命令(用大写N)会将下一文本行添加到模式空间中已有的文后。这样的作用是将数据流中的两个文本行合并到同一个模式空间中。文本行仍然用换行符分隔,但sed编辑器现在会将两行文本当成一行来处理。
如果要在数据文件中查找一个可能会分散在两行中的文本短语的话,这是个很实用的应用程序。这里有个例子。
$ cat data3.txt
On Tuesday, the Linux System
Administrator's group meeting will be held.
All System Administrators should attend.
Thank you for your attendance.
$ sed 'N ; s/System Administrator/Desktop User/' data3.txt
On Tuesday, the Linux System
Administrator's group meeting will be held.
All Desktop Users should attend.
Thank you for your attendance.
替换命令会在文本文件中查找特定的双词短语 System Administrator 。如果短语在一行中的话,事情很好处理,替换命令可以直接替换文本。但如果短语分散在两行中的话,替换命令就没法识别匹配的模式了。
这时 N 命令就可以派上用场了。同时也要加上短语不是分布于两行的情况。
$ sed 'N
> s/System\nAdministrator/Desktop\nUser/
> s/System Administrator/Desktop User/
> ' data3.txt' data3.txt
On Tuesday, the Linux Desktop User's group meeting will be held.
All Desktop Users should attend.
Thank you for your attendance.
用 N 命令将发现第一个单词的那行和下一行合并后,即使短语内出现了换行,你仍然可以找到它。
2 多行删除命令
单行删除命令d
用来删除模式空间中的当前行。但和 N 命令一起使用时,使用单行删除命令就要小心了。
$ sed 'N ; /System\nAdministrator/d' data4.txt
All System Administrators should attend.
删除命令会在不同的行中查找单词System和Administrator,然后在模式空间中将两行都删掉。这未必是你想要的结果。
sed编辑器提供了多行删除命令D
,它只删除模式空间中的第一行。该命令会删除到换行符(含
换行符)为止的所有字符。
$ sed 'N ; /System\nAdministrator/D' data4.txt
Administrator's group meeting will be held.
All System Administrators should attend.
文本的第二行被 N 命令加到了模式空间,但仍然完好。如果需要删掉目标数据字符串所在行的前一文本行,它能派得上用场。
这里有个例子,它会删除数据流中出现在第一行前的空白行。
$ cat data5.txt
This is the header line.
This is a data line.
This is the last line.
$ sed '/^$/{N ; /header/D}' data5.txt
This is the header line.
This is a data line.
This is the last line.
sed编辑器脚本会查找空白行,然后用 N 命令来将下一文本行添加到模式空间。如果新的模式空间内容含有单词header,则 D 命令会删除模式空间中的第一行。如果不结合使用N
命令和D
命令,就不可能在不删除其他空白行的情况下只删除第一个空白行。
3. 多行打印命令
现在,你可能已经了解了单行和多行版本命令间的差异。多行打印命令P
沿用了同样的方法,它只打印多行模式空间中的第一行。这包括模式空间中直到换行符为止的所有字符。当你用-n选项来阻止脚本输出时,它和显示文本的单行p
命令的用法大同小异。
$ sed -n 'N ; /System\nAdministrator/P' data3.txt
On Tuesday, the Linux System
当多行匹配出现时,P
命令只会打印模式空间中的第一行。多行P
命令的强大之处在和N
命令
及D
命令组合使用时才能显现出来。
D
命令的独特之处在于强制sed编辑器返回到脚本的起始处,对同一模式空间中的内容重新执行这些命令(它不会从数据流中读取新的文本行)。在命令脚本中加入N
命令,你就能单步扫过整个模式空间,将多行一起匹配。
使用P
命令打印出第一行,然后用D
命令删除第一行并绕回到脚本的起始处。一旦返
回,N
命令会读取下一行文本并重新开始这个过程。这个循环会一直继续下去,直到数据流结束。
四、保持空间
模式空间(pattern space)是一块活跃的缓冲区,在sed编辑器执行命令时它会保存待检查的文本。但它并不是sed编辑器保存文本的唯一空间。
sed编辑器有另一块称作保持空间(hold space)的缓冲区域。在处理模式空间中的某些行时,可以用保持空间来临时保存一些行。有5条命令可用来操作保持空间。
表1 sed编辑器的保持空间命令
命 令 | 描 述 |
---|---|
h | 将模式空间复制到保持空间 |
H | 将模式空间附加到保持空间 |
g | 将保持空间复制到模式空间 |
G | 将保持空间附加到模式空间 |
x | 交换模式空间和保持空间的内容 |
这些命令用来将文本从模式空间复制到保持空间。这可以清空模式空间来加载其他要处理的字符串。
通常,在使用 h 或 H 命令将字符串移动到保持空间后,最终还要用 g 、 G 或 x 命令将保存的字符串移回模式空间(否则,你就不用在一开始考虑保存它们了)。由于有两个缓冲区域,弄明白哪行文本在哪个缓冲区域有时会比较麻烦。这里有个简短的例子演示了如何用 h 和 g 命令来将数据在sed编辑器缓冲空间之间移动。
sed -n '/first/ {h ; p ; n ; p ; g ; p }' data2.txt
This is the first data line.
This is the second data line.
This is the first data line.
我们来一步一步看上面这个代码例子:
(1) sed脚本在地址中用正则表达式来过滤出含有单词first的行;
(2) 当含有单词first的行出现时,`h`命令将该行放到保持空间;
(3) `p`命令打印模式空间也就是第一个数据行的内容;
(4) `n`命令提取数据流中的下一行( This is the second data line ),并将它放到模式
空间;
(5) `p`命令打印模式空间的内容,现在是第二个数据行;
(6) `g`命令将保持空间的内容( This is the first data line )放回模式空间,替换当
前文本;
(7) `p`命令打印模式空间的当前内容,现在变回第一个数据行了。
通过使用保持空间来回移动文本行,你可以强制输出中第一个数据行出现在第二个数据行后面。如果丢掉了第一个 p 命令,你可以以相反的顺序输出这两行。
```shell
$ sed -n '/first/ {h ; n ; p ; g ; p }' data2.txt
This is the second data line.
This is the first data line.
这是个有用的开端。你可以用这种方法来创建一个sed脚本将整个文件的文本行反转!但要那么做的话,你需要了解sed编辑器的排除特性,也就是下节的内容。
五、排除命令
排除命令用于配置命令使其不要作用到数据流中的特定地址或地址区间。感叹号命令!
用来排除( negate )命令,也就是让原本会起作用的命令不起作用。下面的例子演示了这一特性。
$ sed -n '/header/!p' data2.txt
This is the first data line.
This is the second data line.
This is the last line.
普通p
命令只打印data2文件中包含单词header的那行。加了感叹号之后,情况就相反了:除了包含单词header那一行外,文件中其他所有的行都被打印出来了。
感叹号在有些应用中用起来很方便。之前遇到的一种情况:sed编辑器无法处理数据流中最后一行文本,因为之后再没有其他行了。可以用感叹号来解决这个问题。
$ sed 'N;
> s/System\nAdministrator/Desktop\nUser/
> s/System Administrator/Desktop User/
> ' data4.txt
On Tuesday, the Linux Desktop
User's group meeting will be held.
All System Administrators should attend.
$ sed '$!N;
> s/System\nAdministrator/Desktop\nUser/
> s/System Administrator/Desktop User/
> ' data4.txt
On Tuesday, the Linux Desktop
User's group meeting will be held.
All Desktop Users should attend.
这个例子演示了如何配合使用感叹号与 N 命令以及与美元符特殊地址。美元符表示数据流中的最后一行文本,所以当sed编辑器到了最后一行时,它没有执行 N 命令,但它对所有其他行都执
行了这个命令。
使用这种方法,你可以反转数据流中文本行的顺序。要实现这个效果(先显示最后一行,最
后显示第一行),你得利用保持空间做一些特别的铺垫工作。
你得像这样使用模式空间:
(1) 在模式空间中放置一行;
(2) 将模式空间中的行放到保持空间中;
(3) 在模式空间中放入下一行;
(4) 将保持空间附加到模式空间后;
(5) 将模式空间中的所有内容都放到保持空间中;
(6)重复执行第(3)~(5)步,直到所有行都反序放到了保持空间中;
(7) 提取并打印行。
图21-1详细描述了这个过程。
在使用这种方法时,你不想在处理时打印行。这意味着要使用 sed 的 -n 命令行选项。下一步
是决定如何将保持空间文本附加到模式空间文本后面。这可以用 G 命令完成。唯一的问题是你不
想将保持空间附加到要处理的第一行文本后面。这可以用感叹号命令轻松解决:
1!G
下一步就是将新的模式空间(含有已反转的行)放到保持空间。这也非常简单,只要用 h 命
令就行。
将模式空间中的整个数据流都反转了之后,你要做的就是打印结果。当到达数据流中的最后
一行时,你就知道已经得到了模式空间的整个数据流。打印结果要用下面的命令:
$p
这些都是你创建可以反转行的sed编辑器脚本所需的操作步骤。现在可以运行一下试试:
$ cat data2.txt
This is the header line.
This is the first data line.
This is the second data line.
This is the last line.
$ sed -n '{1!G ; h ; $p }' data2.txt
This is the last line.
This is the second data line.
This is the first data line.
This is the header line.
sed编辑器脚本的执行和预期的一样。脚本输出反转了文本文件中原来的行。这展示了在sed
脚本中使用保持空间的强大之处。它提供了一种在脚本输出中控制行顺序的简单办法。
有个Linux命令已经有反转文本文件的功能了。 tac 命令会倒序显示一个文本文件。这个命令的名字很巧妙,它执行的正好是与 cat 命令相反的功能。
六、分支命令
前面了解到可以使用!来排除不想要操作的行,但如果要对这些行进行其他的操作时就要用到分支命令了.
分支( branch )命令 b 的格式如下:
[ address ]b [ label ]
address 参数决定了哪些行的数据会触发分支命令。 label 参数定义了要跳转到的位置。你可以定义多种label对不同类的行进行不同的操作。
$ sed '{/2/b jump1; /3/b jump2; s/test/No jump/
> :jump1 s/test/Jump1 here/
> :jump2 s/test/Jump2 here/}' test
No jump 1
Jump1 here 2
Jump2 here 3
No jump 4
满足标签12
的,执行s/test/Jump1 here/
命令;满足标签23
的,执行s/test/Jump2 here/
命令;其他的执行s/test/No jump/
命令。