Sed 使用总结

sed 原理与工作流程

sed编辑器称为流编辑器(stream editor),根据命令处理流中的数据,原理:
sed 维护模式空间(pattern space)与保持空间(hold space)两个缓冲区。sed不在原输入上直接进行处理,先将读入的行放到模式空间缓冲区中,对缓冲区里的内容进行处理,处理完毕后也不会写回原文件(除非用shell的输出重定向来保存结果),而是直接输出到屏幕上,之后清空缓冲区,读取下一行继续处理。保持空间是一个辅助缓冲区,在处理模式空间中的某些行时,可以用保持空间来临时保存一些行。sed提供一些命令操作模式空间与保持空间中的数据。

sed 命令格式与选项

sed 命令格式:
sed options script file

可使用的选项:

选项 描述
-e script 在处理输入时,将script中指定的命令添加到已有的命令中
-f file 在处理输入时,将file中指定的命令添加到已有的命令中
-n 不产生命令输出,使用print命令来完成输出

cript参数指定了应用于流数据上的单个命令。如果需要用多个命令,要么使用-e选项在命令行中指定,要么使用-f选项在单独的文件中指定

在命令行定义编辑器命令

默认情况下,sed编辑器会将指定的命令应用到STDIN输入流上。这样你可以直接将数据通过管道输入sed编辑器处理。例如:

$ echo "This is a test" | sed 's/test/big test/'
This is a big test
$

在命令行使用多个编辑器命令

要在sed命令行上执行多个命令时,只要用-e选项就可以了。

$ sed -e 's/brown/green/; s/dog/cat/' data1.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.
$

必须记住,要在封尾单引号所在行结束命令bash shell一旦发现了封尾的单引号,就会执行命令。开始后,sed命令就会将你指定的每条命令应用到文本文件中的每一行上。

从文件中读取编辑器命令

最后,如果有大量要处理的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 基础

更多的替换选项

替换标记

s(substitution)默认情况下只替换每行中出现的第一处。使用替换标记(substitution flag)指定替换位置。替换标记在替换命令字符串之后设置。

s/pattern/replacement/flags

有4种可用的替换标记:

  1. 数字,表明新文本将替换第几处模式匹配的地方;
  2. g,表明新文本将会替换所有匹配的文本;
  3. p,表明替换过的行要打印出来;
  4. w file,将替换的结果写到文件中。
$ sed 's/test/trial/2' data4.txt
This is a test of the trial script.
This is the second test of the trial script.

$ sed 's/test/trial/g' data4.txt
This is a trial of the trial script.
This is the second trial of the trial script.

#p替换标记会打印与替换命令中指定的模式匹配的行。这通常会和sed的-n选项一起使用。
# -n选项将禁止sed编辑器输出。但p替换标记会输出修改过的行。将二者配合使用的效果就是只输出被替换命令修改过的行。
$ cat data5.txt
This is a test line.
This is a different line.
$
$ sed -n 's/test/trial/p' data5.txt
This is a trial line.

#w替换标记会产生同样的输出,不过会将输出保存到指定文件中。
#sed编辑器的正常输出是在STDOUT中,而只有那些包含匹配模式的行才会保存在指定的输出文件中。
$ sed 's/test/trial/w test.txt' data5.txt
This is a trial line.
This is a different line.
$
$ cat test.txt
This is a trial line.
$

替换字符

sed允许选择其他字符来作为替换命令中的字符串分隔符:

$ sed 's!/bin/bash!/bin/csh!' /etc/passwd

使用地址

默认情况下,sed命令会作用于文本数据的所有行。如果只想将命令作用于特定行或某些行,则必须用行寻址(line addressing)。
在sed编辑器中有两种形式的行寻址:

  1. 以数字形式表示行区间
  2. 用文本模式来过滤出行

两种形式都使用相同的格式来指定地址:

[address]command

也可以将特定地址的多个命令分组:

address {
command1
command2
command3
}

数字方式的行寻址

sed编辑器会将文本流中的第一行编号为1。在命令中指定的地址可以是单个行号,或是用起始行号、逗号以及结尾行号指定的一定区间范围内的行。

$ sed '2s/dog/cat/' data1.txt
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy cat
The quick brown fox jumps over the lazy dog
The quick brown fox jumps over the lazy dog

$ sed '2,3s/dog/cat/' data1.txt
The quick brown fox jumps over the lazy dog
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 dog

#如果想将命令作用到文本中从某行开始的所有行,可以用特殊地址——美元符。
$ sed '2,$s/dog/cat/' data1.txt
The quick brown fox jumps over the lazy dog
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编辑器允许指定文本模式来过滤出命令要作用的行。格式如下:

/pattern/command

必须用正斜线将要指定的pattern封起来。sed编辑器会将该命令作用到包含指定文本模式的行上。文本模式可以使用正则表达式。

$ sed '/Samantha/s/bash/csh/' /etc/passwd

命令组合

如果需要在单行上执行多条命令,可以用花括号将多条命令组合在一起。sed编辑器会处理地址行处列出的每条命令。

$ sed '2{
> s/fox/elephant/
> s/dog/cat/
> }' data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown elephant jumps over the lazy cat.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
$
#两条命令都会作用到该地址上。当然,也可以在一组命令前指定一个地址区间。
#sed编辑器会将所有命令作用到该地址区间内的所有行上。
$ sed '3,${
> s/brown/green/
> s/lazy/active/
> }' data1.txt
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick green fox jumps over the active dog.
The quick green fox jumps over the active dog.
$

删除行

删除命令d删除匹配指定寻址模式的所有行。如果没有加入寻址模式,流中的所有文本行都会被删除。

$ cat data1.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 'd' data1.txt
$ sed '3d' data6.txt
$ sed '2,3d' data6.txt
$ sed '3,$d' data6.txt
$ sed '/number 1/d' data6.txt

使用两个文本模式来删除某个区间内的行时,第一个模式会“打开”行删除功能,第二个模式会“关闭”行删除功能。sed编辑器会删除两个指定行之间的所有行(包括指定的行)。只要sed编辑器在数据流中匹配到了开始模式,删除功能就会打开。如果没有找到停止模式,数据流中的剩余行将被全部删除。

$ cat data7.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
This is line number 1 again.
This is text you want to keep.
This is the last line in the file.

$ sed '/1/,/3/d' data7.txt
This is line number 4.

$ sed '/1/,/5/d' data7.txt

插入和附加文本

  1. 插入(insert)命令(i)会在指定行前增加一个新行;
  2. 附加(append)命令(a)会在指定行后增加一个新行。

这两条命令不能在单个命令行上使用。必须指定将行插入或附加到哪一行。格式如下:

sed '[address]command\
new line'
$ echo "Test Line 2" | sed 'i\Test Line 1'
Test Line 1
Test Line 2
$ echo "Test Line 2" | sed 'a\Test Line 1'
Test Line 2
Test Line 1
#在命令行上使用sed时,会看到次提示符来提醒输入新的行数据。必须在该行完成sed编辑器命令。
$ echo "Test Line 2" | sed 'i\
> Test Line 1'
Test Line 1
Test Line 2

可以使用一个数字行号或文本模式指定一个行地址,但不能用地址区间。这合乎逻辑,因为你只能将文本插入或附加到单个行的前面或后面,而不是行区间的前面或后面。
要插入或附加多行文本,就必须对要插入或附加的新文本中的每一行使用反斜线,直到最后一行。

$ sed '3i\
> This is an inserted line.' data6.txt
This is line number 1.
This is line number 2.
This is an inserted line.
This is line number 3.
This is line number 4.

$ sed '3a\
> This is an appended line.' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is an appended line.
This is line number 4.

$ sed '$a\
> This is a new line of text.' data6.txt
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.
This is a new line of text.

$ sed '1i\
> This is one line of new text.\
> This is another line of new text.' data6.txt
This is one line of new text.
This is another line of new text.
This is line number 1.
This is line number 2.
This is line number 3.
This is line number 4.

修改行

修改(change)命令(c)允许修改(替换)数据流中整行文本的内容。如果作用于地址区间,sed会用一行文本代替区间内的所有行,而非逐一替换。

$ sed '3c\
> This is a changed line of text.' data6.txt
This is line number 1.
This is line number 2.
This is a changed line of text.
This is line number 4.

$ sed '/number 3/c\
> This is a changed line of text.' data6.txt
This is line number 1.
This is line number 2.
This is a changed line of text.
This is line number 4.

$ sed '2,3c\
> This is a new line of text.' data6.txt
This is line number 1.
This is a new line of text.
This is line number 4.
$

转换命令

转换(transform)命令(y)是唯一可以处理单个字符的sed编辑器命令。转换命令格式如下:

[address]y/inchars/outchars/

转换命令会对incharsoutchars值进行一对一的映射。如果inchars和outchars的长度不同,sed产生一条错误消息。 转换命令是一个全局命令,会将文本行中找到的所有指定字符自动进行转换,而不考虑它们出现的位置。

$ sed 'y/123/789/' data8.txt
This is line number 7.
This is line number 8.
This is line number 9.
This is line number 4.
This is line number 7 again.
This is yet another line.
This is the last line in the file.
#全局命令
$ echo "This 1 is a test of 1 try." | sed 'y/123/456/'
This 4 is a test of 4 try.
$

回顾打印

3个命令也能用来打印数据流中的信息:

  1. p命令用来打印文本行;
  2. 等号(=)命令用来打印行号;
  3. l(小写的L)命令用来列出行。

打印行

跟替换命令中的p标记类似,p命令可以打印sed编辑器输出中的一行。

$ echo "this is a test" | sed 'p'
this is a test
this is a test
$ sed -n '/number 3/p' data6.txt
$ sed -n '2,3p' data6.txt
#修改之前打印行
$ sed -n '/3/{
> p
> s/line/test/p
> }' data6.txt
this is line number 3.
this is test number 3.

打印行号

等号命令会打印行在数据流中的当前行号。行号由数据流中的换行符决定。

$ cat data1.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 '=' data1.txt
1
The quick brown fox jumps over the lazy dog.
2
The quick brown fox jumps over the lazy dog.
3
The quick brown fox jumps over the lazy dog.
4
The quick brown fox jumps over the lazy dog.

$ sed -n '/number 4/{
> =
> p
> }' data6.txt
4
This is line number 4.

列出行

列出(list)命令(l)可以打印数据流中的文本和不可打印的ASCII字符。任何不可打印字符要么在其八进制值前加一个反斜线,要么使用标准C风格的命名法(用于常见的不可打印字符),比如\t,来代表制表符。

$ cat data9.txt
This line contains tabs.

$ sed -n 'l' data9.txt
This\tline\tcontains\ttabs.$

#制表符的位置使用\t来显示。行尾的美元符表示换行符。如果数据流包含了转义字符,列出命令会在必要时候用八进制码来显示。
$ cat data10.txt
This line contains an escape character.
$
$ sed -n 'l' data10.txt
This line contains an escape character. \a$

data10.txt文本文件包含了一个转义控制码来产生铃声。当用cat命令来显示文本文件时,你看不到转义控制码,只能听到声音(如果你的音箱打开的话)。但是,利用列出命令,你就能显示出所使用的转义控制码。

使用sed处理文件

替换命令包含一些可以用于文件的标记。还有一些sed编辑器命令也可以实现同样的目标,不需要非得替换文本。

写入文件

w命令用来向文件写入行。该命令的格式如下:

[address]w filename

地址可以是sed中支持的任意类型的寻址方式,例如单个行号、文本模式、行区间或文本模式。

$ sed -n '1,2w test.txt' data6.txt
$ cat test.txt
This is line number 1.
This is line number 2.
$ sed -n '/Browncoat/w Browncoats.txt' data11.txt
$ cat Browncoats.txt
Blum, R Browncoat
Bresnahan, C Browncoat

从文件读取数据

读取(read)命令(r)将一个独立文件中的数据插入到数据流中。读取命令的格式如下:

[address]r 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 '/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.

$ 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.

sed 进阶

多行命令

sed 基础命令都是针对单行数据执行操作的。有时需要对跨多行的数据执行特定操作,例如要查找或替换一个跨多行短语。
sed编辑器包含了三个可用来处理多行文本的特殊命令。

  1. N:将数据流中的下一行加进来创建一个多行组(multiline group)来处理。
  2. D:删除多行组中的一行。
  3. P:打印多行组中的一行。

next 命令

单行next命令n会将数据流中的下一文本行移动到sed编辑器的工作空间(称为模式空间),而不用重新回到命令的最开始再执行一遍。通常sed编辑器在移动到数据流中的下一文本行之前,会在当前行上执行完所有定义好的命令。单行next命令改变了这个流程。
例如,数据文件共有5行内容,两行是空的。目标是删除第一个空白行,保留第二个空白行。首先,查找header行, 接着使用n命令移动到下一行,即空行, 最后使用d命令删除行。

$ sed '/header/{n ; d}' data1.txt
This is the header line.
This is a data line.

This is the last line.

多行版本的next命令N会将下一文本行添加到模式空间中已有的文本后。文本行仍然用换行符分隔,但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 '/first/{ N ; s/\n/ / }' data2.txt
This is the header line.
This is the first data line. This is the second data line.
This is the last line.

如果到了最后一行文本,没有下一行可读时,N命令会停止sed编辑器。所以如果要匹配的文本正好在数据流的最后一行上,就不会发现要匹配的数据。可以将单行命令放到N命令前面,并将多行命令放到N命令后面。

$ sed '
> s/System Administrator/Desktop User/
> N
> s/System\nAdministrator/Desktop\nUser/
> ' data4.txt
On Tuesday, the Linux Desktop
User group meeting will be held.
All Desktop Users should attend.

多行删除命令

sed用d命令删除模式空间中的当前行。由于N命令将附加的行与模式空间的行当成一行处理,所以d命令会把多行一起删掉。

$ sed 'N ; /System\nAdministrator/d' data4.txt
All System Administrators should attend.

sed多行删除命令D,它只删除模式空间中的第一行。该命令会删除到换行符(含换行符)为止的所有字符。

$ sed 'N ; /System\nAdministrator/D' data4.txt
Administrator group meeting will be held.
All System Administrators should attend.

多行打印命令

多行打印命令(P)只打印多行模式空间中的第一行。这包括模式空间中直到换行符为止的所有字符。当你用-n选项来阻止脚本输出时,它和显示文本的单行p命令的用法大同小异。

$ sed -n 'N ; /System\nAdministrator/P' data3.txt
On Tuesday, the Linux System

保持空间

模式空间(pattern space)是一块活跃的缓冲区,在sed执行命令时它会保存待检查的文本。sed有另一块称作保持空间(hold space)的缓冲区域(默认是一个空行的空间,及第一次执行H时,前面有一行空行)。在处理模式空间中的某些行时,可以用保持空间来临时保存一些行。有5条命令可用来操作保持空间,见下表。

命令 描 述
h 将模式空间复制到保持空间
H 将模式空间附加到保持空间
g 将保持空间复制到模式空间
G 将保持空间附加到模式空间
x 交换模式空间和保持空间的内容
$ 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 '/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命令打印模式空间的当前内容,现在变回第一个数据行了。

排除命令

排除(negate)命令(!)的作用是使命令不要作用到数据流中的特定地址或地址区间。

$ sed -n '/header/!p' data2.txt
This is the first data line.
This is the second data line.
This is the last line.

之前演示了一种情况:sed的N命令无法处理数据流中最后一行文本,因为之后再没有其他行了。可以用感叹号来解决这个问题。

$ sed 'N;
> s/System\nAdministrator/Desktop\nUser/
> s/System Administrator/Desktop User/
> ' data4.txt
On Tuesday, the Linux Desktop
User 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 group meeting will be held.
All Desktop Users should attend.

这个例子演示了如何配合使用!N命令以及与$$表示数据流中的最后一行文本,所以当sed编辑器到了最后一行时,它没有执行N命令,但它对所有其他行都执行了这个命令。使用这种方法,你可以反转数据流中文本行的顺序。要实现这个效果,你得利用保持空间做一些特别的铺垫工作。
你得像这样使用模式空间:

  1. 在模式空间中放置一行;
  2. 将模式空间中的行放到保持空间中;
  3. 在模式空间中放入下一行;
  4. 将保持空间附加到模式空间后;
  5. 将模式空间中的所有内容都放到保持空间中;
  6. 重复执行第(3)~(5)步,直到所有行都反序放到了保持空间中;
  7. 提取并打印行。

在使用这种方法时,你不想在处理时打印行。这意味着要使用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编辑器会从脚本的顶部开始,一直执行到脚本的结尾(D命令是个例外,它会强制sed编辑器返回到脚本的顶部,而不读取新的行)。sed编辑器提供了一个方法来改变命令脚本的执行流程,其结果与结构化编程类似。

分支

sed编辑器提供了一种方法,可以基于地址、地址模式或地址区间排除一整块命令。这允许你只对数据流中的特定行执行一组命令。
分支(branch)命令b的格式如下:
[address]b [label]
address参数决定了哪些行的数据会触发分支命令。label参数定义了要跳转到的位置。如果没有加label参数,跳转命令会跳转到脚本的结尾。

$ 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 '{2,3b ; s/This is/Is this/ ; s/line./test?/}' data2.txt
Is this the header test?
This is the first data line.
This is the second data line.
Is this the last test ?
$

分支命令在数据流中的第2行和第3行处跳过了两个替换命令。
要是不想直接跳到脚本的结尾,可以为分支命令定义一个要跳转到的标签。标签以冒号开始,最多可以是7个字符长度。
:label2
要指定标签,将它加到b命令后即可。使用标签允许你跳过地址匹配处的命令,但仍然执行脚本中的其他命令。

$ sed '{/first/b jump1 ; s/This is the/No jump on/
> :jump1
> s/This is the/Jump here on/}' data2.txt
No jump on header line
Jump here on first data line
No jump on second data line
No jump on last line
$

如果某行匹配了分支模式,sed编辑器就会跳转到带有分支标签的那行。如果没有匹配,会继续执行脚本中的命令,包括分支标签后的命令。 可以跳转到脚本中靠前面的标签上,这样就达到了循环的效果。例如:

$ echo "This, is, a, test, to, remove, commas." | sed -n '{
> :start
> s/,//1p
> b start
> }'
This is, a, test, to, remove, commas.
This is a, test, to, remove, commas.
This is a test, to, remove, commas.
This is a test to, remove, commas.
This is a test to remove, commas.
This is a test to remove commas.
^C
$

脚本的每次迭代都会删除文本中的第一个逗号,并打印字符串。这个脚本有个问题:它永远不会结束。这就形成了一个无穷循环,不停地查找逗号,直到使用Ctrl+C组合键发送一个信号,手动停止这个脚本。要防止这个问题,可以为分支命令指定一个地址模式来查找。如果没有模式,跳转就应该结束。

$ echo "This, is, a, test, to, remove, commas." | sed -n '{
> :start
> s/,//1p
> /,/b start
> }'
This is, a, test, to, remove, commas.
This is a, test, to, remove, commas.
This is a test, to, remove, commas.
This is a test to, remove, commas.
This is a test to remove, commas.
This is a test to remove commas.
$

现在分支命令只会在行中有逗号的情况下跳转。在最后一个逗号被删除后,分支命令不会再执行,脚本也就能正常停止了。

测试

类似于分支命令,测试(test)命令(t)也可以用来改变sed编辑器脚本的执行流程。测试命令会根据替换命令的结果跳转到某个标签,而不是根据地址进行跳转。如果替换命令成功匹配并替换了一个模式,测试命令就会跳转到指定的标签。如果替换命令未能匹配指定的模式,测试命令就不会跳转而是继续执行剩下的脚本。测试命令使用与分支命令相同的格式。
[address]t [label]
跟分支命令一样,在没有指定标签的情况下,如果测试成功,sed会跳转到脚本的结尾。 测试命令提供了对数据流中的文本执行基本的if-then语句的一个低成本办法。举个例子, 如果已经做了一个替换,不需要再做另一个替换,那么测试命令能帮上忙。

$ sed '{
> s/first/matched/
> t
> s/This is the/No match on/
> }' data2.txt
No match on header line
This is the matched data line
No match on second data line
No match on last line
$
$ echo "This, is, a, test, to, remove, commas. " | sed -n '{
> :start
> s/,//1p
> t start
> }'
This is, a, test, to, remove, commas.
This is a, test, to, remove, commas.
This is a test, to, remove, commas.
This is a test to, remove, commas.
This is a test to remove, commas.
This is a test to remove commas.
$

模式替代

sed使用模式来替代数据流中的文本。然而在使用通配符时,很难知道到底哪些文本会匹配模式。例如:

$ echo "The cat sleeps in his hat." | sed 's/cat/"cat"/'
The "cat" sleeps in his hat.
$

#用通配符(.)来匹配多个单词
$ echo "The cat sleeps in his hat." | sed 's/.at/".at"/g'
The ".at" sleeps in his ".at".
$

模式字符串用点号通配符来匹配at前面的一个字母。遗憾的是,用于替代的字符串无法匹配已匹配单词中的通配符字符。

&符号

&符号可以用来代表替换命令中的匹配的模式。

$ echo "The cat sleeps in his hat." | sed 's/.at/"&"/g'
The "cat" sleeps in his "hat".
$

子模式

&符号会提取匹配替换命令中指定模式的整个字符串。
sed编辑器用圆括号来定义替换模式中的子模式。你可以在替代模式中使用特殊字符来引用 每个子模式。替代字符由反斜线和数字组成。数字表明子模式的位置。sed编辑器会给第一个子 模式分配字符\1,给第二个子模式分配字符\2,依此类推。

警告 当在替换命令中使用圆括号时,必须用转义字符将它们标示为分组字符而不是普通的圆 括号。这跟转义其他特殊字符正好相反。

$ echo "The System Administrator manual" | sed '
> s/\(System\) Administrator/\1 User/'
The System User manual
$

子模式在处理通配符模式时特别有用。如果需要用一个单词来替换一个短语,而这个单词刚好是该短语的子字符串,但那个子字符 串碰巧使用了通配符,这时使用子模式会方便很多。

$ echo "That furry cat is pretty" | sed 's/furry \(.at\)/\1/'
That cat is pretty
$
$ echo "That furry hat is pretty" | sed 's/furry \(.at\)/\1/'
That hat is pretty
$

在这种情况下,你不能用&符号,因为它会替换整个匹配的模式。子模式提供了答案,允许你选择将模式中的某部分作为替代模式。 当需要在两个或多个子模式间插入文本时,这个特性尤其有用。这里有个脚本,它使用子模式在大数字中插入逗号。

$ echo "1234567" | sed '{
> :start
> s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/
> t start
> }'
1,234,567

这个脚本将匹配模式分成了两部分。
.*[0-9]
[0-9]{3}
这个模式会查找两个子模式。第一个子模式是以数字结尾的任意长度的字符。第二个子模式是若干组三位数字。如果这个模式在文本中找到了,替代文本会在两个子模式之间加一个逗号,每个子模式都会通过其位置来标示。这个脚本使用测试命令来遍历这个数字,直到放置好所有的逗号。(正则表达式默认会尽可能多的消耗字符)

在shell脚本中使用sed

使用shell脚本

将sed命令放到shell脚本(wrapper)中,可以将普通的shell变量及参数和sed编辑器脚本一起使用。

$ cat reverse.sh
#!/bin/bash
# Shell wrapper for sed editor script.
# to reverse text file lines.
sed -n '{ 1!G ; h ; $p }' $1
#
$
$ ./reverse.sh 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会将脚本的结果输出到STDOUT上。在shell脚本中使用各种标准方法对sed编辑器的输出进行重定向。 可以在脚本中用$()将sed编辑器命令的输出重定向到一个变量中,以备后用。下面的例子使用sed脚本来向数值计算结果添加逗号。

$ cat fact.sh
#!/bin/bash
# Add commas to number in factorial answer
#
factorial=1
counter=1
number=$1
#
while [ $counter -le $number ]
do
factorial=$[ $factorial * $counter ]
counter=$[ $counter + 1 ]
done
#
result=$(echo $factorial | sed '{
:start
s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/
t start
}')
#
echo "The result is $result"
#
$
$ ./fact.sh 20
The result is 2,432,902,008,176,640,000
$

在使用普通的阶乘计算脚本后,脚本的结果会被作为sed编辑器脚本的输入,它会给结果加上逗号。然后echo语句使用这个值产生最终结果。

创建sed 实用工具

可以使用sed进行大量很酷的数据格式化工作。本节展示一些方便趁手、众所周知的sed脚本,帮助我们进行常见的数据处理工作。

加倍行间距

向文本文件的行间插入空白行。

#去掉最后一行空白行
$ sed '$!G' data2.txt
This is the header line.

This is the first data line.

This is the second data line.

This is the last line.
$

看起来相当简单!这个技巧的关键在于保持空间的默认值。记住,G命令会简单地将保持空间内容附加到模式空间内容后。当启动sed编辑器时,保持空间只有一个空行。将它附加到已有行后面,你就在已有行后面创建了一个空白行。

加倍行间距2

如果文本文件已经有一些空白行,但你想给所有行加倍行间距要怎么办呢?如果用前面的脚本,有些区域会有太多的空白行,因为每个已有的空白行也会被加倍。这个问题的解决办法是,首先删除数据流中的所有空白行,然后用G命令在所有行后插入新的空白行。

$ sed '/^$/d ; $!G' data6.txt
This is line one.
This is line two.
This is line three.
This is line four.
$

给文件中的行编号

在获得了等号命令的输出之后,通过管道将输出传给另一个sed脚本,使用N命令来合并这两行并用替换命令将换行符更换成空格或制表符。

$ sed '=' data2.txt | sed 'N; s/\n/ /'
1 This is the header line.
2 This is the first data line.
3 This is the second data line.
4 This is the last line.
$

打印末尾行

滚动窗口是检验模式空间中文本行块的常用方法,它使用N命令将这些块合并起来,一旦你在模式空间有了一个10行的文本块,你可 以用美元符来检查你是否已经处于数据流的尾部。如果不在,就继续向模式空间增加行,同时删除原来的行(记住,D命令会删除模式空间的第一行)。 通过循环N命令和D命令,你在向模式空间的文本行块增加新行的同时也删除了旧行。分支命令非常适合这个循环。要结束循环,只要识别出最后一行并用q命令退出就可以了。

$ cat data7.txt
This is line 1.
This is line 2.
This is line 3.
This is line 4.
This is line 5.
This is line 6.
This is line 7.
This is line 8.
This is line 9.
This is line 10.
This is line 11.
This is line 12.
This is line 13.
This is line 14.
This is line 15.
$
$ sed '{
> :start
> $q ; N ; 11,$D
> b start
> }' data7.txt
This is line 6.
This is line 7.
This is line 8.
This is line 9.
This is line 10.
This is line 11.
This is line 12.
This is line 13.
This is line 14.
This is line 15.
$

如果当前行在第10行后面,11,$D命令会删除模式空间中的第一行。

删除行

另一个有用的sed编辑器工具是删除数据流中不需要的空白行。删除数据流中的所有空白行 很容易,但要选择性地删除空白行则需要一点创造力。本节将会给出一些简短的sed编辑器脚本, 它们可以用来帮助删除数据中不需要的空白行。

删除连续的空白行

删除连续空白行的关键在于创建包含一个非空白行和一个空白行的地址区间。如果sed编辑器遇到了这个区间,它不会删除行。下面是完成这个操作的脚本。
/./,/^$/!d
区间是/.//^$/。区间的开始地址会匹配任何含有至少一个字符的行。区间的结束地址会匹配一个空行。在这个区间内的行不会被删除。

$ cat data8.txt
This is line one.


This is line two.

This is line three.



This is line four.
$
$ sed '/./,/^$/!d' data8.txt
This is line one.

This is line two.

This is line three.

This is line four.
删除开头的空白行

删除数据流顶部的空白行不难。下面是完成这个功能的脚本。
/./,$!d

$ cat data9.txt


This is line one.

This is line two.
$
$ sed '/./,$!d' data9.txt
This is line one.

This is line two.
删除结尾的空白行

就跟打印数据流的结尾一样,删除数据流结尾的空白行也需要花点心思,利用循环来实现。

sed '{
:start
/^\n*$/{$d; N; b start }
}'

注意,在正常脚本的花括号里还有花括号。这允许你在整个命令脚本中将一些命令分组。该命令组会被应用在指定的地址模式上。地址模式能够匹配只含有一个换行符的行。如果找到了这样的行,而且还是最后一行,删除命令会删掉它。如果不是最后一行,N命令会将下一行附加到它后面,分支命令会跳到循环起始位置重新开始。

$ cat data10.txt
This is the first line.
This is the second line.



$ sed '{
> :start
> /^\n*$/{$d ; N ; b start }
> }' data10.txt
This is the first line.
This is the second line.
删除HTML 标签

如果使用 s/<.*>//g,将删除<>之前的所有内容,包括<,>,如下所示。
应该让sed忽略掉任何嵌入到原始标签中的大于号,使用s/<[^>]*>//g,并且用/^$/删除多余的空行

$ cat data11.txt
<html>
<head>
<title>This is the page title</title>
</head>
<body>
<p>
This is the <b>first</b> line in the Web page.
This should provide some <i>useful</i>
information to use in our sed script.
</body>
</html>
$
$ sed 's/<.*>//g' data11.txt






This is the line in the Web page.
This should provide some
information to use in our sed script.


$
$ sed 's/<[^>]*>//g ; /^$/d' data11.txt
This is the page title
This is the first line in the Web page.
This should provide some useful
information to use in our sed script.