感谢支持
我们一直在努力

Linux高级文本处理工具之sed

sedStream Editor文本流编辑,sed是一个“非交互式的”面向字符流的编辑器。能同时处理多个文件多行的内容,可以不对原文件改动,把整个文件输入到屏幕,可以把只匹配到模式的内容输入到屏幕上。还可以对原文件改动,但是不会再屏幕上返回结果。

基本概念

一.sed命令的语法如下所示:

sed [options] script filename

sed命令的选项(option):

-n :只打印模式匹配的行

-e :多脚本运行,多点编辑,例如 -e script1 -e script2 -e script3

-f :将sed的动作写在一个文件内,用–f filename 执行filename内的sed动作

-r :支持扩展表达式

-i :直接修改文件内容

同大多数Linux命令一样,sed也是从stdin中读取输入,并且将输出写到stdout,但是当filename被指定时,则会从指定的文件中获取输入,输出可以重定向到文件中,但是需要注意的是,该文件绝对不能与输入的文件相同。

options是指sed的命令行参数,这一块并不是重点,参数也不多。

script是指需要对输入执行的一个或者多个操作指令(instruction),sed会依次读取输入文件的每一行到缓存中并应用script中指定的操作指令,因此而带来的变化并不会影响最初的文件(注:如果使用sed时指定-i参数则会影响最初的文件)。如果操作指令很多,为了不影响可读性,可以将其写到文件中,并通过-f参数指定scriptfile:

sed -f scriptfile filename

说明:

这里有一个建议,在命令行中指定的操作指令最好用单引号引起来,这样可以避免shell对特殊字符的处理。

二、sed工作原理

1.读入新的一行内容到缓存空间;

2.从指定的操作指令中取出第一条指令,判断是否匹配pattern;

3.如果不匹配,则忽略后续的编辑命令,回到第2步继续取出下一条指令;

4.如果匹配,则针对缓存的行执行后续的编辑命令;完成后,回到第2步继续取出下一条指令;

5.当所有指令都应用之后,输出缓存行的内容;回到第1步继续读入下一行内容;

6.当所有行都处理完之后,结束;

sed工作原理图:

4164cac228056582.png

bce97006a016dfa0.jpg

三、简单例子

实例1:将MA替换为Massachusetts

[root@localhost ~]# cat list
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

[root@localhost ~]# sed -e 's@MA@Massachusetts@' list
John Daggett, 341 King Road, Plymouth Massachusetts
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury Massachusetts
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston Massachusetts

实例2:这里面的-e选项是可选的,这个参数只是在命令行中同时指定多个操作指令时才需要用到

[root@localhost ~]# sed -e 's/ MA/, Massachusetts/' -e 's/ PA/, Pennsylvania/' list 
John Daggett, 341 King Road, Plymouth, Massachusetts
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls, Pennsylvania
Eric Adams, 20 Post Road, Sudbury, Massachusetts
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston, Massachusetts

即使在多个操作指令的情况下,-e参数也不是必需的,我一般不会加-e参数,比如上面的例子可以换成下面的写法:

[root@localhost ~]# sed 's/ MA/, Massachusetts/;s/ PA/, Pennsylvania/' list

说明:操作指令之间可以用逗号分隔,这点和shell命令可以用逗号分隔是一样的。

实例3:只输出修改过的内容

[root@localhost ~]# sed -n 's@MA@Massachusetts@p' list 
John Daggett, 341 King Road, Plymouth Massachusetts
Eric Adams, 20 Post Road, Sudbury Massachusetts
Sal Carpenter, 73 6th Street, Boston Massachusetts

说明sed命令是指定-n参数,该参数会抑制sed默认的输出

模式空间与地址匹配

一、模式空间的转换

sed只会缓存一行的内容在模式空间,这样的好处是sed可以处理大文件而不会有任何问题,不像一些编辑器因为要一次性载入文件的一大块内容到缓存中而导致内存不足。下面用一个简单的例子来讲解模式空间的转换过程,如下图所示:

ff755bdbb54b32a4.png

现在要把一段文本中的Unix System与UNIX System都要统一替换成The UNIX Operating System,因此我们用两句替换命令来完成这个目的:

s/Unix /UNIX /
s/UNIX System/UNIX Operating System/

对应上图,过程如下:

1.首先一行内容The Unix System被读入模式空间;

2.应用第一条替换命令将Unix替换成UNIX;

3.现在模式空间的内容变成The UNIX System;

4.应用第二条替换命令将UNIX System替换成UNIX Operating System;

5.现在模式空间的内容变成The UNIX Operating System;

6.所有编辑命令执行完毕,默认输出模式空间中的行;

二、地址匹配

默认情况下,sed是全局匹配的,即对所有输入行都应用指定的编辑命令,这是因为sed依次读入每一行,每一行都会成为当前行并被处理,所以s/CA/California/g会将所有输入行的CA替换成California。这一点跟vi/vim是不一样的,众所周知,vim的替换命令默认是替换当前行的内容,除非你指定%s才会作全局替换。

实例1:将list文件中包含Sal的行中MA替换为Massachusetts

[root@localhost ~]# sed -e /Sal/'s@MA@Massachusetts@' list 
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston Massachusetts

说明:/Sal/是一个正则表达式匹配包含Sal的行

/Sal/是一个正则表达式匹配包含Sal的行,因此像行“San Francisco, CA”则不会被替换。

sed命令中可以包含0个、1个或者2个地址(地址对),地址可以为正则表达式(如/Sal/),行号或者特殊的行符号(如$表示最后一行):

● 如果没有指定地址,默认将编辑命令应用到所有行;

●如果指定一个地址,只将编辑命令应用到匹配该地址的行;

●如果指定一个地址对(addr1,addr2),则将编辑命令应用到地址对中的所有行(包括起始和结束);

●如果地址后面有一个感叹号(!),则将编辑命令应用到不匹配该地址的所有行;

实例2:为了方便理解上述内容,我们以删除命令(d)为例,默认不指定地址将会删除所有行

[root@localhost ~]# sed 'd' list
[root@localhost ~]#

实例3:删除制定的行

[root@localhost ~]# cat -n list
 1  John Daggett, 341 King Road, Plymouth MA
 2  Alice Ford, 22 East Broadway, Richmond VA
 3  Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
 4  Terry Kalkas, 402 Lans Road, Beaver Falls PA
 5  Eric Adams, 20 Post Road, Sudbury MA
 6  Hubert Sims, 328A Brook Road, Roanoke VA
 7  Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
 8  Sal Carpenter, 73 6th Street, Boston MA

[root@localhost ~]# sed '1d' list  #删除list文件的第一行
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

[root@localhost ~]# sed '$d' list #删除list文件的最后一行
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA

[root@localhost ~]# sed /MA/'d' list   #删除包含MA的行
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA

[root@localhost ~]# sed '/MA/d' list  #同上,也是删除包含MA的行
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA

实例4:通过指定地址对可以删除该范围内的所有行,例如删除第3行到最后一行

[root@localhost ~]# cat list
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA
[root@localhost ~]# sed '2,$d' list
John Daggett, 341 King Road, Plymouth MA

实例5:使用正则匹配,删除从包含Alice的行开始到包含Hubert的行结束的所有行

[root@localhost ~]# cat list
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA
[root@localhost ~]# sed '/Alice/,/Hubert/d' list
John Daggett, 341 King Road, Plymouth MA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

实例6:行号和地址对是可以混用的

[root@localhost ~]# cat -n list
     1  John Daggett, 341 King Road, Plymouth MA
     2  Alice Ford, 22 East Broadway, Richmond VA
     3  Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
     4  Terry Kalkas, 402 Lans Road, Beaver Falls PA
     5  Eric Adams, 20 Post Road, Sudbury MA
     6  Hubert Sims, 328A Brook Road, Roanoke VA
     7  Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
     8  Sal Carpenter, 73 6th Street, Boston MA
[root@localhost ~]# sed '2,/Amy/d' list   #删除第二行到Amy之间的所有行
John Daggett, 341 King Road, Plymouth MA
Sal Carpenter, 73 6th Street, Boston MA

实例7:如果在地址后面指定感叹号(!),则会将命令应用到不匹配该地址的行

[root@localhost ~]# sed '1,3!d' list   #表示删除1到3行以外的行
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK

实例8:执行多个编辑命令,sed中可以用{}来组合命令,就好比编程语言中的语句块

[root@localhost ~]# cat -n list
     1  John Daggett, 341 King Road, Plymouth MA
     2  Alice Ford, 22 East Broadway, Richmond VA
     3  Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
     4  Terry Kalkas, 402 Lans Road, Beaver Falls PA
     5  Eric Adams, 20 Post Road, Sudbury MA
     6  Hubert Sims, 328A Brook Road, Roanoke VA
     7  Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
     8  Sal Carpenter, 73 6th Street, Boston MA
[root@localhost ~]# sed -n '1,4{s/ MA/, Massachusetts/;s/ PA/, Pennsylvania/;p}' list
John Daggett, 341 King Road, Plymouth, Massachusetts
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls, Pennsylvania

实例9:显示list文件中的奇数行

[root@localhost ~]# sed -n '1~2p' list  #1~2表示从第一行开始步进单位为2行
John Daggett, 341 King Road, Plymouth MA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Eric Adams, 20 Post Road, Sudbury MA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA

实例10:显示list文件中的偶数行

     [root@localhost ~]# cat -n list
     1  John Daggett, 341 King Road, Plymouth MA
     2  Alice Ford, 22 East Broadway, Richmond VA
     3  Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
     4  Terry Kalkas, 402 Lans Road, Beaver Falls PA
     5  Eric Adams, 20 Post Road, Sudbury MA
     6  Hubert Sims, 328A Brook Road, Roanoke VA
     7  Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
     8  Sal Carpenter, 73 6th Street, Boston MA

    [root@localhost ~]# sed -n '2~2p' list
    Alice Ford, 22 East Broadway, Richmond VA
    Terry Kalkas, 402 Lans Road, Beaver Falls PA
    Hubert Sims, 328A Brook Road, Roanoke VA
    Sal Carpenter, 73 6th Street, Boston MA

实例11:显示list文件中从第6行开往后的三行

[root@localhost ~]# sed -n '6,+3p' list
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

说明:#,+n表明从数字#开始后边的n行

更多详情见请继续阅读下一页的精彩内容: http://www.linuxidc.com/Linux/2017-02/140315p2.htm

基础命令

以下是要介绍的全部基础命令:

d83c0151ba6be192.png

一、替换命令: s

替换命令的语法是:

[address]s/pattern/replacement/flags

其中[address]是指地址,pattern是替换命令的匹配表达式,replacement则是对应的替换内容,flags是指替换的标志位,它可以包含以下一个或者多个值:

● n: 一个数字(取值范围1-512),表明仅替换第n个被pattern匹配的内容;

● g: 表示全局替换,替换所有被pattern匹配的内容;

● p: 仅当行被pattern匹配时,打印模式空间的内容;

● w file:仅当行被pattern匹配时,将模式空间的内容输出到文件file中;

实例1:如果flags为空,则默认替换第一次匹配

[root@localhost ~]# echo 'a b c d' | sed 's/ /;/'
a;b c d

实例2:flags中包含g,则表示全局匹配

[root@localhost ~]# echo 'a b c d'|sed 's/ /;/g'
a;b;c;d

实例3:flags中明确指定替换第n次的匹配,例如n=2

[root@localhost ~]# echo 'a b c d'|sed 's/ /;/2' 
a b;c d

实例4:当替换命令的pattern与地址部分是一样的时候,比如/regexp/s/regexp/replacement/可以省略替换命令中的pattern部分。现在要在substitute command后面增加(“s”),同时在被修改的行前面增加+号

[root@localhost ~]# cat -nE paragraph.txt                           
 1  The substitute command is applied to the lines matching the address. If no address is specified, it is applied to all lines that match the pattern, a regular expression. If a regular expression is supplied as an address, and no pattern is specified, the substitute command matches what is matched by the address. This can be useful when the substitute command is one of multiple commands applied at the same address. For an example, see the section “Checking Out Reference Pages” later in this chapter.$

[root@localhost ~]# sed '/substitute command/{s//&("s")/g;s/^/+ /}' paragraph.txt 
+ The substitute command("s") is applied to the lines matching the address. If no address is specified, it is applied to all lines that match the pattern, a regular expression. If a regular expression is supplied as an address, and no pattern is specified, the substitute command("s") matches what is matched by the address. This can be useful when the substitute command("s") is one of multiple commands applied at the same address. For an example, see the section “Checking Out Reference Pages” later in this chapter.

说明:replacement部分用到了&这个元字符,它代表之前匹配的内容

在replacemnt部分中也有几个特殊的元字符,它们分别是:

● &: 被pattern匹配的内容

●\num: 被pattern匹配的第num个分组(正则表达式中的概念,..括起来的部分称为分组)

●\: 转义符号,用来转义&,\, 回车等符号

实例5:修改上面的配置来减少开机启动的时候创建的tty个数,比如我们只想要2个

[root@localhost ~]# grep -B 1 ACTIVE_CONSOLES /etc/sysconfig/init
# What ttys should gettys be started on?
ACTIVE_CONSOLES=/dev/tty[1-6]

[root@localhost ~]# sed -r -i 's@(ACTIVE_CONSOLES=/dev/tty\[1-)6\]@\12\]@' /etc/sysconfig/init

[root@localhost ~]# What ttys should gettys be started on?
ACTIVE_CONSOLES=/dev/tty[1-2]

说明:其中-i参数表示直接修改原文件,-r参数是指使用扩展的正则表达式(ERE),扩展的正则表达式中分组的括号不需要用反斜杠转义。这里 [ 是有特殊含义的(表示字符组),所以需要转义。在替换的内容中使用\1来引用这个匹配的分组内容,1代表分组的编号,表示第一个分组。

二、删除命令: d

删除命令的语法是:

[address]d

删除命令可以用于删除多行内容,例如1,3d会删除1到3行。删除命令会将模式空间中的内容全部删除,并且导致后续命令不会执行并且读入新行,因为当前模式空间的内容已经为空。

实例1:

[root@localhost ~]# sed '2,$d;=' list  #删除并打印行号
1
John Daggett, 341 King Road, Plymouth MA

实例2:

[root@localhost ~]# sed '=;2,$d' list
1
John Daggett, 341 King Road, Plymouth MA
2
3
4
5
6
7
8

实例3:

[root@localhost ~]# sed '2,${d;=}' list  
John Daggett, 341 King Road, Plymouth MA

实例4:

[root@localhost ~]# sed -e '2,$d' -e '1=' list
1
John Daggett, 341 King Road, Plymouth MA

说明:仔细对比实例1,2,3,4之间的区别,实例1是先读取list文件中第一行,不符合第二行到最后一行的匹配条件,因此不执行d删除命令,继续执行;注意分号标志此命令语句执行结束;=命令执行的条件无地址定界默认全文,因此匹配模式,继续处理上条d删除命令的输出结果,执行=打印行号命令。实例3的{}相当一个语句块,语句块中的命令相当于对匹配到第二行到最后一行的内容执行。

三、插入行/追加行/替换行命令: i/a/c

这三个命令的语法如下所示:

# Append 追加
[line-address]atext
# Insert 插入
line-address]itext
# Change 行替换 
[address]ctext

说明:以上三个命令,行替换命令(c)允许地址为多个地址,其余两个都只允许单个地址

追加命令是指在匹配的行后面插入文本text;相反地,插入命令是指匹配的行前面插入文本text;最后,行替换命令会将匹配的行替换成文本text。文本text并没有被添加到模式空间,而是直接输出到屏幕,因此后续的命令也不会应用到添加的文本上。注意,即使使用-n参数也无法抑制添加的文本的输出。

实例1:在list文件中第二行后面添加 ‘——‘

[root@localhost ~]# cat list
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

[root@localhost ~]# sed  '2a-------------------------------------------' list
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
-------------------------------------------
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

实例2:在list文件中第三行前添加’——–‘

[root@localhost ~]# sed '3i----------------------------------------' list
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
----------------------------------------
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
Terry Kalkas, 402 Lans Road, Beaver Falls PA
Eric Adams, 20 Post Road, Sudbury MA
Hubert Sims, 328A Brook Road, Roanoke VA
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
Sal Carpenter, 73 6th Street, Boston MA

我们来测试下文本是否确实没有添加到模式空间,因为模式空间中的内容默认是会打印到屏幕的:

[root@localhost ~]# sed -n '2a-------------' list
-------------

通过-n参数来抑制输出后发现插入的内容依然被输出,所以可以判定插入的内容没有被添加到模式空间。

实例3:使用行替换命令将第2行到最后一行的内容全部替换成’-—-‘

[root@localhost ~]# sed '2,$c------------------' list
John Daggett, 341 King Road, Plymouth MA
------------------

四、打印命令: p/l/=

这里纯粹的打印命令应该是指p,但是因为后两者(l和=)和p差不多,并且相对都比较简单,所以这里放到一起介绍。

这三个命令的语法是:

[address]p
[address]=
[address]l

p命令用于打印模式空间的内容,它不清除模式空间,也不改变脚本控制流程,例如打印list文件的第一行:

实例1:

    [root@localhost ~]# cat -n list
     1  John Daggett, 341 King Road, Plymouth MA
     2  Alice Ford, 22 East Broadway, Richmond VA
     3  Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
     4  Terry Kalkas, 402 Lans Road, Beaver Falls PA
     5  Eric Adams, 20 Post Road, Sudbury MA
     6  Hubert Sims, 328A Brook Road, Roanoke VA
     7  Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
     8  Sal Carpenter, 73 6th Street, Boston MA

    [root@localhost ~]# sed -n '1p' list #打印list文件第一行
    John Daggett, 341 King Road, Plymouth MA

l命令类似p命令,不过会显示控制字符,这个命令和vim的list命令相似。

实例2:

[root@localhost ~]# echo -e 'a\n b\n c d' | sed -n 'l'
a$
 b$
 c d$

=命令显示当前行行号。

实例3:

[root@localhost ~]# sed '=' list
1
John Daggett, 341 King Road, Plymouth MA
2
Alice Ford, 22 East Broadway, Richmond VA
3
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
4
Terry Kalkas, 402 Lans Road, Beaver Falls PA
5
Eric Adams, 20 Post Road, Sudbury MA
6
Hubert Sims, 328A Brook Road, Roanoke VA
7
Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
8
Sal Carpenter, 73 6th Street, Boston MA

五、转换命令:y

转换命令的语法是:

[address]y/SET1/SET2/

它的作用是在匹配的行上,将SET1中出现的字符替换成SET2中对应位置的字符,例如1,3y/abc/xyz/会将1到3行中出现的a替换成x,b替换成y,c替换成z。是不是觉得这个功能很熟悉,其实这一点和tr命令是一样的。可以通过y命令将小写字符替换成大写字符。

实例1:

[root@localhost ~]# echo "hello, world" | sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'
HELLO, WORLDHELLO, WORLD

说明:SET1、SET2不支持模式匹配

六、取下一行命令: n

取下一行命令的语法为:

[address]n

n命令为将下一行的内容提前读入,并且将之前读入的行(在模式空间中的行)输出到屏幕,然后后续的命令会应用到新读入的行上。因此n命令也会同d命令一样改变sed的控制流程。

实例1:将text文件中.H1后面的空行删除

[root@localhost ~]# cat text 
.H1 "On Egypt"

Napoleon, pointing to the Pyramids, said to his troops:
"Soldiers, forty centuries have their eyes upon you."
[root@localhost ~]# sed '/.H1/{n;/^$/d}' text 
.H1 "On Egypt"
Napoleon, pointing to the Pyramids, said to his troops:
"Soldiers, forty centuries have their eyes upon you."

注意:此处只能使用 / ,不能使用@、#等符号。

七、读写文件命令: r/w

读写文件命令的语法是:

[line-address]r file
[address]w file

读命令将指定的文件读取到匹配行之后,并且输出到屏幕,这点类似追加命令(a)。

实例1:将包含公司名称列表的文件内容读取到text文件中[company-list]之后

[root@localhost ~]# cat text                                    
For service, contact any of the following companies:
[Company-list]
Thank you.
[root@localhost ~]# cat company.list                            
Allied
Mayflower
United
[root@localhost ~]# sed '/^\[Company-list\]/r company.list' text #注意需要对[]进行转义
For service, contact any of the following companies:
[Company-list]
Allied
Mayflower
United
Thank you.

实例2:读取实例1的文件并且删除[Company-list]这一行

[root@localhost ~]# sed -e '/^\[Company-list\]/r company.list' -e '/^\[Company-list\]/d;' text   
For service, contact any of the following companies:
Allied
Mayflower
United
Thank you.

注意:此处不能使用分号连接,如下所示

[root@localhost ~]# sed '/^\[Company-list\]/r company.list;/^\[Company-list\]/d;' text
For service, contact any of the following companies:
[Company-list]
Thank you.

因为r命令会把company.list;/^[Company-list]/d;整体作为文件名称识别,此文件名不存在所以读取操作认为文件不存在不会报错,原样输出。

实例3:将text文件中不同区域的人名写到不同的文件当中

[root@localhost ~]# cat text 
Adams, Henrietta Northeast
Banks, Freda South
Dennis, Jim Midwest
Garvey, Bill Northeast
Jeffries, Jane West
Madison, Sylvia Midwest
Sommes, Tom South

[root@localhost ~]# sed '/Northeast$/w region.northeast
/South$/w region.south
/Midwest$/w region.midwest
/West$/w region.west' text
Adams, Henrietta Northeast
Banks, Freda South
Dennis, Jim Midwest
Garvey, Bill Northeast
Jeffries, Jane West
Madison, Sylvia Midwest
Sommes, Tom South
[root@localhost ~]# ls region.*
region.midwest  region.northeast  region.south  region.west
[root@localhost ~]# cat region.*
Dennis, Jim Midwest
Madison, Sylvia Midwest
Adams, Henrietta Northeast
Garvey, Bill Northeast
Banks, Freda South
Sommes, Tom South
Jeffries, Jane West

八、退出命令: q

退出命令的语法:

[line-address]q

当sed读取到匹配的行之后即退出,不会再读入新的行,并且将当前模式空间的内容输出到屏幕。

实例1:打印list文件前三行的内容

[root@localhost ~]# cat -n list #查看list内容
     1  John Daggett, 341 King Road, Plymouth MA
     2  Alice Ford, 22 East Broadway, Richmond VA
     3  Orville Thomas, 11345 Oak Bridge Road, Tulsa OK
     4  Terry Kalkas, 402 Lans Road, Beaver Falls PA
     5  Eric Adams, 20 Post Road, Sudbury MA
     6  Hubert Sims, 328A Brook Road, Roanoke VA
     7  Amy Wilde, 334 Bayshore Pkwy, Mountain View CA
     8  Sal Carpenter, 73 6th Street, Boston MA
[root@localhost ~]# sed '3q' list 
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK

实例2:使用p命令打印前三行内容

[root@localhost ~]# sed -n '1,3p' list
John Daggett, 341 King Road, Plymouth MA
Alice Ford, 22 East Broadway, Richmond VA
Orville Thomas, 11345 Oak Bridge Road, Tulsa OK

说明:但是对于大文件来说,实例1比实例2效率更高,因为实例1读取到第N行之后就退出了。实例2虽然打印了前N行,但是后续的行还是要继续读入,只不会不作处理。

sed高级命令

sed允许将多行内容读取到模式空间,这样你就可以匹配跨越多行的内容。本篇笔记主要介绍这些命令,它们能够创建多行模式空间并且处理之。其中,N/D/P这三个多行命令分别对应于小写的n/d/p命令,后者我们在上一篇已经介绍。它们的功能是类似的,区别在于命令影响的内容不同。例如D命令与d命令同样是删除模式空间的内容,只不过d命令会删除模式空间中所有的内容,而D命令仅会删除模式空间中的第一行。

一、读下一行:N

N命令将下一行的内容读取到当前模式空间,但是与n命令不一样的地方是N命令并没有直接输出当前模式空间中的行,而是把下一行追加到当前模式空间,两行之间用回车符\n连接,如下图所示:

2681f3d240d18017.png

说明:模式空间包含多行之后,正则表达式的^/$符号的意思就变了,^是匹配模式空间的最开始而非行首,$是匹配模式空间的最后位置而非行尾。

实例1:替换以下文本中的”Owner and Operator Guide”为”Installation Guide”

[root@localhost ~]# cat text 
Consult Section 3.1 in the Owner and Operator
Guide for a description of the tape drives
available on your system.

[root@localhost ~]# sed '/Operator$/{N;s/Owner and Operator\nGuide/Installation Guide/}' text
Consult Section 3.1 in the Installation Guide for a description of the tape drives
available on your system.

不过这个例子有两个局限:

● 我们知道Owner and Operator Guide分割的位置;

● 执行替换命令后,前后两行拼接在一起,导致这行过长;

第二点,可以这样解决:

[root@localhost ~]# sed '/Operator$/{N;s/Owner and Operator\nGuide/Installation Guide\n/}' text
Consult Section 3.1 in the Installation Guide
 for a description of the tape drives
available on your system.

实例2:Owener and Operator Guide出现在多行的多个位置

[root@localhost ~]# cat text   #原文文本
Consult Section 3.1 in the Owner and Operator
Guide for a description of the tape drives
available on your system.

Look in the Owner and Operator Guide shipped with your system.

Two manuals are provided including the Owner and
Operator Guide and the User Guide.

The Owner and Operator Guide is shipped with your system.


[root@localhost ~]# sed 's/Owner and Operator Guide/Installation Guide/
>  /Owner/{
>  N
>  s/ *\n/ /
>  s/Owner and Operator Guide */Installation Guide>  /
> }' text
Consult Section 3.1 in the Installation Guide
 for a description of the tape drives
available on your system.

Look in the Installation Guide shipped with your system.

Two manuals are provided including the Installation Guide
 and the User Guide.

The Installation Guide is shipped with your system.

说明:这里我们首先将在单行出现的Owner and Operator Guide替换为Installation Guide,然后再寻找匹配Owner的行,匹配后读取下一行的内容到模式空间,并且将中间的换行符替换成空格,最后再替换Owner and Operator Guide。

看上去sed命令中作了两次替换是多余的。实际上,如果去掉第一次替换,再运行脚本,就会发现输出存在两个问题。一个是结果中最后一行不会被替换(在某些版本的sed中甚至不会被输出)。这是因为最后一行匹配了”Owner”,执行N命令,但是已经到了文件末尾,某些版本就会直接打印这行再退出,而另外一些版本则是不作出打印立即退出。对于这个问题可以通过命令”$!N”来解决。这表示N命令对最后一行不起作用。另外一个问题是”look manuals”一段被拆为两行,而且与下一段的空行被删除了。这是因为内嵌的换行符被替换的结果。因此,sed中做两次替换一点也不是多余的。

二、删除行:D

该命令删除模式空间中第一行的内容,而它对应的小d命令删除模式空间的所有内容。D不会导致读入新行,相反它会回到最初的编辑命令,重要应用在模式空间剩余的内容上。

实例1:现在我们要删除文件text多余的空行,将多个空行缩减成一行。

文件内容:

[root@localhost ~]# cat text 
This line is followed by 1 blank line.

This line is followed by 2 blank line.


This line is followed by 3 blank line.



This line is followed by 4 blank line.




This is the end.

使用d命令删除如下:

[root@localhost ~]# sed '/^$/{N;/^\n$/d}' text 
This line is followed by 1 blank line.

This line is followed by 2 blank line.
This line is followed by 3 blank line.

This line is followed by 4 blank line.
This is the end.

说明:我们会发现一个奇怪的结果,奇数个数的相连空行已经被合并成一行,但是偶数个数的却全部被删除了。造成这样的原因需要重新翻译下上面的命令,当匹配一个空行是,将下一行也读取到模式空间,然后若下一行也是空行,则模式空间中的内容应该是\n,因此匹配^\n$,从而执行d命令会将模式空间中的内容清空,结果就是相连的两个空行都被删除。这样就可以理解为什么相连奇数个空行的情况下是正常的,而偶数个数就有问题了。

使用D命令删除如下:

[root@localhost ~]# sed '/^$/{N;/^\n$/D}' text 
This line is followed by 1 blank line.

This line is followed by 2 blank line.

This line is followed by 3 blank line.

This line is followed by 4 blank line.

This is the end.

说明:D命令只会删除模式空间的第一行,而且删除后会重新在模式空间的内容上执行编辑命令,类似形成一个循环,前提是相连的都是空行。当匹配一个空行时,N读取下一行内容,此时匹配^\n$导致模式空间中的第一行被删除。现在模式空间中的内容是空的,重新执行编辑命令,此时匹配/^$/。继续读取下一行,当下一行依然为空行时,重复之前的动作,否则输出当前模式空间的内容。造成的结果是连续多个空行,只有最后一个空行是保留输出的,其余的都被删除了。这样的结果才是我们最初希望得到的。

三、打印行:P

P命令与p命令一样是打印模式空间的内容,不同的是前者仅打印模式空间的第一行内容,而后者是打印所有的内容。因为编辑命令全部执行完之后,sed默认会输出模式空间的内容,所以一般情况下,p和P命令都是与-n选项一起使用的。但是有一种情况是例外的,即编辑命令的执行流程被改变的情况,例如N,D等。很多情况下,P命令都是用在N命令之后,D命令之前的。这三个命令合起来,可以形成一人输入输出的循环,并且每次只打印一行:读入一行后,N继续读下一行,P命令打印第一行,D命令删除第一行,执行流程回到最开始重复该过程。

实例1:

[root@localhost ~]# echo -e "line1nline2nline3" | sed '$!N;P;D'
line1nline2nline3
[root@localhost ~]# echo -e "line1\nline2\nline3" | sed '$!N;P;D'
line1
line2
line3

实例2:

[root@localhost ~]# echo -e "line1\nline2\nline3" | sed -n 'N;1P'

说明:你可能期望打印第一行的内容,事实上并没有输出。原因是当N继续读入第二行后,当前行号已经是2了,行号只是sed在内部维护的一个计数变量而已,每当读入新的一行,行号就加一。

实例3:

[root@localhost ~]# echo -e "line1\nline2\nline3" | sed -n '$!N;='
2
3

实例4:N/P/D三个命令是如何配合使用

[root@localhost ~]# cat -n f1
     1  The UNIX
     2  System and UNIX
     3  ...

[root@localhost ~]# sed '/UNIX$/{N;s/\nSystem/ Operating &/;P;D}' f1
The UNIX Operating 
System and UNIX
...

实例4执行流程图:

708ef6d602efe28a.png

模式空间是sed内部维护的一个缓存空间,它存放着读入的一行或者多行内容。但是模式空间的一个限制是无法保存模式空间中被处理的行,因此sed又引入了另外一个缓存空间——模式空间(Hold Space)。

一、保持空间

保持空间用于保存模式空间的内容,模式空间的内容可以复制到保持空间,同样地保持空间的内容可以复制回模式空间。sed提供了几组命令用来完成复制的工作,其它命令无法匹配也不能修改模式空间的内容。

操作保持空间的命令如下所示:

444b9f37e491249d.png

这几组命令提供了保存、取回以及交换三个动作,交换命令比较容易理解,保存命令和取回命令都有大写和小写两种形式,这两种形式的区别是小写的是将会覆盖目的空间的内容,而大写的是将内容追加到目的空间,追加的内容和原有的内容是以\n分隔。

二、基本使用

实例1:h命令使用

[root@localhost ~]# cat text 
1
2
11
22
111
222

[root@localhost ~]# sed 'h' text 
1
2
11
22
111
222

实例2:G命令使用

[root@localhost ~]# sed 'G' text 
1

2

11

22

111

222

[root@localhost ~]#

说明:实例1返回结果正常,因为复制到保持空间的内容并没有取回;实例2每一行的后面都多了一个空行,原因是每行都会从保持空间取回一行,追加(大写的G)到模式空间的内容之后,以\n分隔。

实例3:使用x命令交换空间

[root@localhost ~]# sed 'x' text 

1
2
11
22
111

a.当读入第一行的时候,模式空间中的内容是第一行的内容,而保持空间是空的,这个时候交换两个空间,导致模式空间为空,保持空间为第一行的内容,因此输出为空行;

b.当读入下一行之后,模式空间为第2行的内容,保持空间为第一行的内容,交换后输出第1行的内容;依次读入每一行,输出上一行的内容;

c.直到最后一行被读入到模式空间,交换后输出倒数第二行的内容,而最后一行的内容并没有输出,此时命令执行结束。

高级命令执行过程图解:

c338b66f8093f8d5.png

sedsed调试过程:

[root@localhost ~]# sedsed -d 'H;g' num
PATT:a$
HOLD:$
COMM:H
PATT:a$
HOLD:\na$
COMM:g
PATT:\na$
HOLD:\na$
a
PATT:b$
HOLD:\na$
COMM:H
PATT:b$
HOLD:\na\nb$
COMM:g
PATT:\na\nb$
HOLD:\na\nb$
a
b
PATT:c$
HOLD:\na\nb$
COMM:H
PATT:c$
HOLD:\na\nb\nc$
COMM:g
PATT:\na\nb\nc$
HOLD:\na\nb\nc$
a
b
c

三、流程控制命令

为了使使用者在书写sed脚本的时候真正的”自由”,sed还允许在脚本中用”:”设置记号,然后用”b”和”t”命令进行流程控制。顾名思义,”b”表示”branch”,”t”表示”test”;前者就是分支命令,后者则是测试命令。 首先来看标签的各式是什么。这个标签放置在你希望流程所开始的地方,单独放一行,以冒号开始。冒号与标签之间不允许有空格或者制表符,标签最后如果有空格的话,也会被认为是标签的一部分。 再来说b命令。它的格式是这样的:

[address]b[label]

它的含意是,如果满足address,则sed流程跟随标签跳转:如果标签指明的话,脚本首先假设这个标签在b命令以下的某行,然后转入该行执行相应的命令;如果这个标签不存在的话,控制流程就直接跳到脚本的末尾。否则继续执行后续的命令。 在某些情况下,b命令和!命令有些相似,但是!命令只能对紧挨它的{}中的内容起作用,而b命令则给予使用者足够的自由在sed脚本中选择哪些命令应该被执行,哪些命令不应该被执行。下面提供几种b命令的

经典用法:

(1) 创建循环:

:top
command1
command2
/pattern/b top
command3

(2) 忽略某些不满足条件的命令:

command1
/patern/b end
command2
:end
command3

(3) 命令的两个部分只能执行其中一个:

command1
/pattern/b dothere
command
b
:dothere
command3

实例1:将把 empnametitle.txt 文件中的雇员名称和职位合并到一行内,字段之间以分号:
分隔,并且在管理者的名称前面加上一个星号
*

[root@localhost ~]# cat emp.txt               
John Doe
CEO
Jason Smith
IT Manager
Raj Reddy
Sysadmin
Anand Ram
Developer
Jane Miller
Sales Manager
[root@localhost ~]# cat label.sed 
N
s/\n/:/
/Manager/!b end
s/^/*/
:end
p
[root@localhost ~]# sed -nf label.sed emp.txt   
John Doe:CEO
*Jason Smith:IT Manager
Raj Reddy:Sysadmin
Anand Ram:Developer
*Jane Miller:Sales Manager

t命令的格式和b命令是一样的:

[address]t[label]

它表示的是如果满足address的话,sed脚本就会根据t命令指示的标签进行流程转移。命令 t 的作用是,如果前面的命令执行成功,那么就跳转到 t 指定的标签处,继续往下执行
后续命令。 否则,仍然继续正常的执行流程。

下面也给出一个例子:

s/pattern/replacement/
t break
command
:break

实例2:将把 empnametitle.txt 文件中的雇员名称和职位合并到一行内,字段之间以分号:
分隔,并且在管理者的名称前面加上三个星号
*

[root@localhost ~]# cat emp.txt 
John Doe
CEO
Jason Smith
IT Manager
Raj Reddy
Sysadmin
Anand Ram
Developer
Jane Miller
Sales Manager
[root@localhost ~]# cat t.label 
h;n;H;x    #此处等同于N命令的作用效果
s/\n/:/
:repeat
/Manager/s/^/*/
/\*\*\*/!t repeat
p
[root@localhost ~]# sed -nf t.label emp.txt 
John Doe:CEO
***Jason Smith:IT Manager
Raj Reddy:Sysadmin
Anand Ram:Developer
***Jane Miller:Sales Manager

注意:分支命令b用于无条件转移,测试命令t用于有条件转移,他们只有当替换命令改变当前行时才会执行。

四、sed实战

实例1:使用sed模拟出tac的功能(倒序输出)

[root@localhost ~]# cat num
1
2
3
4
5
[root@localhost ~]# sed '1!G;h;$!d' num
5
4
3
2
1

实例2:行列转换

[root@localhost ~]# cat num
    1
    2
    3
    4
    5

[root@localhost ~]# sed -ne 'H;${x;s/\n/ /g;p}' num
 1 2 3 4 5

实例3:行列转换100之内数字求和

[root@localhost ~]# seq 100|sed -ne 'H;${x;s/\n/+/g;s/^+//p}'|bc
5050

实例4:追加匹配行到行尾

[root@localhost ~]# cat file
0
1 hello
2
3 hello
4
5
[root@localhost ~]# sed -e '/hello/{H;d}' -e '$G' file 
0
2
4
5

1 hello
3 hello

实例5:在文件中每行后面加空白行

[root@localhost ~]# cat num
1
2
3
4
5
[root@localhost ~]# sed 'G' num
1

2

3

4

5

[root@localhost ~]#

实例6:保留文件最后一行

[root@localhost ~]# sed '$!d' num
5

实例7:保留文件的每一行后方有且只有一个空白行

[root@bash ~]# cat /etc/issue
\S
Kernel \r on an \m

Geek Young Learning Services
http://www.geekyoung.com

[root@bash ~]# sed '/^$/d;G' /etc/issue
\S

Kernel \r on an \m

Geek Young Learning Services

http://www.geekyoung.com

实例8:保留奇数行

[root@localhost ~]# cat num
1
2
3
4
5
[root@localhost ~]# sed 'n;d' num
1
3
5

实例9:去基名

[root@localhost ~]# echo '/etc/sysconfig/network-scripts'|sed -r 's@^.*/([^/]+)/?$@\1@'
network-scripts

实例10:取目录名

[root@localhost ~]# echo '/etc/sysconfig/network-scripts/'|sed -r 's@[^/]+/?$@@'
/etc/sysconfig/

例11:引用系统变量

[root@localhost ~]# a=2
[root@localhost ~]# cat num
a
b
c
[root@localhost ~]# sed -n ''$a',3p' num
b
c

总结:sed高级用法灵活多变,使用前要搞清楚模式空间的内容,以及sed命令的执行过程,到此sed用法就结束了,要想灵活使用sed还得多练。

本文永久更新链接地址:http://www.linuxidc.com/Linux/2017-02/140315.htm

赞(0) 打赏
转载请注明出处:服务器评测 » Linux高级文本处理工具之sed
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏