shell三剑客之sed

Posted 喝茶等下班

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell三剑客之sed相关的知识,希望对你有一定的参考价值。

1.

流编辑器,过滤和替换文本

工作原理:sed命令将当前处理的行读入模式空间进行处理,处理完把结果输出,并清空模式空间。然后再将下一行计入模式空间进行处理输出,以此类推,直到最后一行。还有一个空间叫保持空间,又称暂存空间,可以暂时存放一些处理的数据,但不能直接输出,只能放到模式空间输出。这两个空间其实就是在内存中初始化的一个内存区域,存放正在处理的数据和临时存放的数据。

选项    描述

-e 执行脚本 、表达式来处理

-f 执行运作从文件读取执行

-r 使用扩展正则表达式

2.

命令    描述

p 打印当前模式空间

P 打印模式空间的第一行

d 删除模式空间,开始下一个循环

D 删除模式空间的第一行,开始下一个循环

= 打印当前行号

a \\text 当前行追加文本

i \\text 当选行上面插入文本

c \\text 所选行替换新文本

q 立即退出sed脚本

r 追加文本来自文件

: label label为b和t命令

b label 分支到脚本中带有标签的位置,如果分支不存在则分支到脚本的末尾

t  label 如果s///是一个成功的替换,才跳转到标签

h H 复制/追加模式空间到保持空间

g G 复制/追加保持空间到模式空间

x  交换模式空间和保持空间内容

l  打印模式空间的行,并显示控制字符$

n N  读取/追加下一行输入到模式空间

w filename 写入当前模式空间到文件

!  取反、否定

& 引用已匹配字符串

3.

地址      描述

first~step 步长,每step行,从第first开始

$  匹配最后一行

/regexp/  正则表达式匹配行

number 只匹配指定行

addr1,addr2 开始匹配addr1行开始,直到addr2行结束

addr1,+N 从addr1行开始,向后的N行

addr1,~N 从addr1行开始,到N行结束

4.

打印相关实例

[root@study ~]# tail /etc/services
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
blp5 48129/tcp # Bloomberg locator
blp5 48129/udp # Bloomberg locator
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject
matahari 49000/tcp # Matahari Broker

对比不加-n和加-n

[root@study ~]# tail /etc/services|sed  /^blp5/p
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
blp5 48129/tcp # Bloomberg locator
blp5 48129/tcp # Bloomberg locator
blp5 48129/udp # Bloomberg locator
blp5 48129/udp # Bloomberg locator
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject
matahari 49000/tcp # Matahari Broker
[root@study ~]# tail /etc/services|sed -n /^blp5/p
blp5 48129/tcp # Bloomberg locator
blp5 48129/udp # Bloomberg locator

打印第一行

[root@study ~]# tail /etc/services|sed -n  1p
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol

只打印奇数行

[root@study ~]# tail /etc/services|sed -n  1~2p
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/udp # Image Systems Network Services
blp5 48129/udp # Bloomberg locator
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/udp # iqobject

打印匹配行,及以后一行

[root@study ~]# tail /etc/services|sed -n  /^blp5/,+1p
blp5 48129/tcp # Bloomberg locator
blp5 48129/udp # Bloomberg locator

不打印最后一行,感叹号是取反

[root@study ~]# tail /etc/services|sed -n  $!p
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
blp5 48129/tcp # Bloomberg locator
blp5 48129/udp # Bloomberg locator
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject

引用变量,下面两种写法效果一样的

[root@study ~]# a=1
[root@study ~]# tail /etc/services|sed -n $a,3p
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
[root@study ~]# tail /etc/services|sed -n "$a,3p"
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services

删除是d,打印是把匹配的打印出来,删除是把匹配的删除,删除只是不再用-n选项

5.

替换 

替换开头是blp5的字符串,并打印

[root@study ~]# tail /etc/services |sed -n s/^blp5/test/p
test 48129/tcp # Bloomberg locator
test 48129/udp # Bloomberg locator

使用&命令引用匹配内容并替换

[root@study ~]# tail /etc/services |sed  s/48129/&.0/
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
blp5 48129.0/tcp # Bloomberg locator
blp5 48129.0/udp # Bloomberg locator
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject
matahari 49000/tcp # Matahari Broker

IP加双引号,下面两种写法效果一样的

[root@study ~]# echo 10.10.10.1 10.10.10.2 10.10.10.3|sed -r s/[^ ]+/"&"/g
"10.10.10.1" "10.10.10.2" "10.10.10.3"
[root@study ~]# echo 10.10.10.1 10.10.10.2 10.10.10.3|sed -r s/[! ]+/"&"/g
10.10.10.1" "10.10.10.2" "10.10.10.3

多重编辑

[root@study ~]# tail /etc/services |sed s/blp5/test/;s/3g/4g/
4gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
test 48129/tcp # Bloomberg locator
test 48129/udp # Bloomberg locator
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject
matahari 49000/tcp # Matahari Broker

注,上面的还可以写成

[root@study ~]# tail /etc/services |sed -e s/blp5/test/ -e s/3g/4g/
4gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
test 48129/tcp # Bloomberg locator
test 48129/udp # Bloomberg locator
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject
matahari 49000/tcp # Matahari Broker

分组使用,加上test

[root@study ~]# tail /etc/services |sed -r s/(.*)(.*)(#.*)/\\1\\2test \\3/
3gpp-cbsp 48049/tcp test # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp test # Image Systems Network Services
isnetserv 48128/udp test # Image Systems Network Services
blp5 48129/tcp test # Bloomberg locator
blp5 48129/udp test # Bloomberg locator
com-bardac-dw 48556/tcp test # com-bardac-dw
com-bardac-dw 48556/udp test # com-bardac-dw
iqobject 48619/tcp test # iqobject
iqobject 48619/udp test # iqobject
matahari 49000/tcp test # Matahari Broker

将协议与端口号位置调换

[root@study ~]# tail /etc/services |sed -r s/(.*)(\\<[0-9]+)\\/(tcp|udp)(.*)/\\1\\3\\/\\2\\4/
3gpp-cbsp tcp/48049 # 3GPP Cell Broadcast Service Protocol
isnetserv tcp/48128 # Image Systems Network Services
isnetserv udp/48128 # Image Systems Network Services
blp5 tcp/48129 # Bloomberg locator
blp5 udp/48129 # Bloomberg locator
com-bardac-dw tcp/48556 # com-bardac-dw
com-bardac-dw udp/48556 # com-bardac-dw
iqobject tcp/48619 # iqobject
iqobject udp/48619 # iqobject
matahari tcp/49000 # Matahari Broker

注意:(\\<[0-9]+)这里的\\<表示的是<,作用是匹配字符串的开始,而不是行的开始,与^不同,要注意。

替换x为X

[root@study ~]# echo "abc cde xyz"|sed -r s/(.*)x/\\1X/
abc cde Xyz

位置调换

[root@study ~]# echo "abc:cde;123:456"|sed -r s/([^:]+)(;.*:)([^:]+$)/\\3\\2\\1/
abc:456;123:cde

注:上面的如果分开来理解,容易理解错,从直觉上看,第一个分组([^:]+)匹配到abc,第二个分组(;.*:)匹配;123:,第三个分组([^:]+$)匹配到456,那么\\3\\2\\1的顺序,不应该是456;123:abc吗。其实不是的,首先这个匹配要从整体上来考虑,也就是三个分组是一起连续匹配,就是说第一个匹配非:字符,后面必须连着接着第二个分组即;123:,然后再紧接着第三个分组即456,这样的话abc后面是:而不是;123:,所以就第一个分组继续区配非:的字符串cde,cde后面紧接着匹配第二个分组;123:,那么第一个分组就是cde了,而不是abc。

注释匹配后的多少行

[root@study ~]# seq 10 |sed /5/,+3s/^/#/
1
2
3
4
#5
#6
#7
#8
9
10

注释指定的多行

[root@study ~]# seq 5 |sed -r s/^3|^4/&#/
1
2
3#
4#
5

去掉开头和结尾的空格

[root@study ~]# echo    1 2 3   |sed s/^\\s*//;s/\\s*$//
1 2 3

添加新内容a、i、c,注意添加的内容前面要加个反斜杠,a是追加i是插入c是替换

[root@study ~]# tail /etc/services |sed /blp5/i \\test
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
test
blp5 48129/tcp # Bloomberg locator
test
blp5 48129/udp # Bloomberg locator
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject
matahari 49000/tcp # Matahari Broker

替换blp5替换新行

[root@study ~]# tail /etc/services |sed /blp5/c \\test
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
test
test
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject
matahari 49000/tcp # Matahari Broker

在指定行下一行添加一行

[root@study ~]# tail -5 /etc/services |sed 2a \\test
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
test
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject
matahari 49000/tcp # Matahari Broker

在指定行的前一行,后一行分别插入内容

[root@study ~]# seq 5|sed 3s/.*/txt\\n&/
1
2
txt
3
4
5
[root@study ~]# seq 5|sed 3s/.*/&\\ntxt/
1
2
3
txt
4
5

读取文件并追加到匹配行后:

[root@study ~]# tail /etc/services |sed /blp5/r a.txt
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
blp5 48129/tcp # Bloomberg locator
123
456
blp5 48129/udp # Bloomberg locator
123
456
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject
matahari 49000/tcp # Matahari Broker

将匹配行写入文件

[root@study ~]# tail /etc/services |sed /blp5/w b.txt
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
blp5 48129/tcp # Bloomberg locator
blp5 48129/udp # Bloomberg locator
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject
matahari 49000/tcp # Matahari Broker
[root@study ~]# cat b.txt
blp5 48129/tcp # Bloomberg locator
blp5 48129/udp # Bloomberg locator

以下属于sed的高级部分:

6.

读取下一行(n和N)

n读取下一行到模式空间

N追加下一行内容到模式空间,并以换行符\\n分隔

打印匹配的下一行

[root@study ~]# seq 5 |sed -n /3/n;p
4

打印偶数

[root@study ~]# seq 6 |sed -n n;p
2
4
6

sed先读取第一行1,执行n命令,获取下一行2,此时模式空间是2,执行p命令,打印模式空间。现在模式空间是2,sed再读取3,执行n命令,获取下一行4,此时模式空间为4,执行p命令,以此类推。

打印奇数

[root@study ~]# seq 6 |sed n;d
1
3
5

sed先读取第一行1,此时模式空间是1,并打印模式空间1,执行n命令,获取下一行2,执行d命令,删除模式空间2,sed再读取3,此时模式空间是3,并打印模式空间,再执行n命令,获取下一行4,执行d命令,删除模式空间的3,以此类推。

打印奇数

[root@study ~]# seq 6 |sed -n p;n
1
3
5

每三行执行一次p命令

[root@study ~]# seq 6|sed n;n;p
1
2
3
3
4
5
6
6

第三行替换一次

#方法1
[root@study ~]# seq 6|sed n;n;s/^/=/;s/$/=/
1
2
=3=
4
5
=6=
#方法2,用地址匹配来实现相同效果
[root@study ~]# seq 6|sed 3~3s/^/=/;s/$/=/
1
2
=3=
4
5
=6=
#方法3
[root@study ~]# seq 6|sed -r n;n;s/^|$/=/
1
2
=3
4
5
=6

当执行多个sed命令时,有时相互会产生影响,我们可以用大括号把他们括起来

[root@study ~]# seq 6|sed N;q
1
2
[root@study ~]# seq 6|sed N;s/\\n//
12
34
56

第一个命令:sed读取第一行1,N命令读取下一行2,并以\\n2追加,此时模式空间是1\\n2,再执行q退出。

为了进一步说明N的功能,看第二个命令:执行N命令后,此时模式空间是1\\n2,再执行把\\n替换为空,此时模式空间是12,并打印。

[root@study ~]# seq 5|sed -n N;p
1
2
3
4
[root@study ~]# seq 6|sed -n N;p
1
2
3
4
5
6

为什么第一个不打印5呢?

因为N命令是读取下一行追加到sed读取的当前行,当N读取下一行没有内容时,则退出,也不会执行p命令打印当前行

当行数为偶数时,N始终就能读到下一行,所以也会执行p命令。

[root@study ~]# seq 5|sed -n $!N;p
1
2
3
4
5

如上,想打印奇数行的最后一行,加一个满足条件,当sed执行到最后一行时,用感叹号不去执行N命令,随后执行p命令。

7.

打印和删除模式空间第一行(P和D)

P 打印模式空间的第一行

D 删除模式空间的第一行

打印奇数

[root@study ~]# seq 6|sed -n N;P
1
3
5

保留最后一行

[root@study ~]# seq 6|sed N;D
6

读取第一行1,执行N命令读取下一行并追加到模式空间,此时模式空间是1\\n2,执行D命令删除模式空间第一行1,剩余2.

读取第二行,执行N命令,此时模式空间是3\\n4,执行D命令删除模式空间第一行3,剩余4.

以此类推,读取最后下一行打印时,而N获取不到下一行则退出,不再执行D,因此模式空间只剩余6就打印

你可能会问,不是剩余2剩余4了吗,它们怎么不打印?是这样的,sed取下一行之前,把模式空间的内容打印到标准输出,并且清除模式空间的内容。

8.

保持空间操作

h 复制模式空间内容到保持空间(覆盖)。

H 复制模式空间内容追加到保持空间。

g 复制保持空间内容到模式空间(覆盖)。

G 复制保持空间内容追加到模式空间。

x 模式空间与保持空间内容互换。

将匹配的内容覆盖到另一个匹配

[root@study ~]# seq 6|sed -e /3/h;d -e /5/g
1
2
4
3
6

h命令把匹配的3复制到保持空间,d命令删除模式空间的3.后面命令再对模式空间匹配5,并用g命令把保持空间3覆盖模式空间5

将匹配的内容放到最后

[root@study ~]# seq 6|sed -e /3/h;d -e $G
1
2
4
5
6
3

交换模式空间和保持空间

[root@study ~]# seq 6|sed -e /3/h;d -e /5/x -e $G
1
2
4
3
6
5

看后面的命令,在模式空间匹配5并将保持空间的3与5交换,5就变成了3,最后把保持空间的5追加到模式空间。

倒叙输出

[root@study ~]# seq 5 |sed 1!G;h;$!d
5
4
3
2
1

shell三剑客之sed_sed

每行后添加新行

[root@study ~]# seq 5|sed G
1

2

3

4

5

打印匹配行的上一行内容

[root@study ~]# seq 5|sed -n /3/x;p;h
2

shell三剑客之sed_3g_02

其实执行x;p;q也可以,这样当执行到匹配行的时候,后面h就不再执行了q是退出

打印匹配行到最后一行

[root@study ~]# seq 5|sed -n /3/,$p
3
4
5
[root@study ~]# seq 5|sed -n /3/,$h;x;p
3
4
5
[root@study ~]# seq 5|sed -n /3/:a;N;$!ba;p
3
4
5

打印匹配行下一行到最后一行

[root@study ~]# seq 5|sed -n /3/n;:a;N;$!ba;p
4
5

匹配到3时,n读取下一行4,此时模式空间是4,执行N命令读取下一行并追加到模式空间,此时模式空间是4\\n5,标签循环完成后打印模式空间4\\n5。

9.

标签(:、b和t)

标签可以控制流,实现分支判断。

: lable name 定义标签

b lable 跳转到指定标签,如果没有标签则到脚本末尾

t  label 跳转到指定标签,前提是s///命令执行成功

将换行符替换成逗号

[root@study ~]# seq 6|sed  N;s/\\n/,/
1,2
3,4
5,6

这种方式并不能满足我们的需求,每次sed读取到模式空间再打印是新行,替换\\n也只能对N命令追加后的1\\n2这样替换。

这时就可以用到标签了:

[root@study ~]# seq 6|sed :a;N;s/\\n/,/;ba
1,2,3,4,5,6

先将每行读入到模式空间,最后再执行全局替换。$!是如果是最后一行,则不执行b a跳转,最后执行全局替换

[root@study ~]# seq 6|sed :a;N;b a;s/\\n/,/g
1
2
3
4
5
6

可以看到,不加$!是没有替换,因为循环到N命令没有读到行就退出了,后面的替换也就没执行。

这里,我想到了一个不用标签就能成功的,就是先通过H,复制追加到保持空间,到执行到最后一行的时候,交换保持空间与模式空间,然后执行全局替换换行符为逗号,然后打印出全部模式空间,与是如下:

[root@study ~]# seq 5|sed -n H;$x;s/\\n/,/g;p
,1,2,3,4,5

差一点就成功了,不明白为什么1前面还要个逗号,说明1前面有个换行符,为什么会有换行符?百度了下,在

​sed的模式空间和保持空间​​ 看到了sed命令定义

+ g:[address[,address]]g 将hold space中的内容拷贝到pattern space中,原来pattern space里的内容清除。

+ G:[address[,address]]G 将hold space中的内容append到pattern space\\n后。

+ h:[address[,address]]h 将pattern space中的内容拷贝到hold space中,原来的hold space里的内容被清除。

+ H:[address[,address]]H 将pattern space中的内容append到hold space\\n后。

+ d:[address[,address]]d 删除pattern中的所有行,并读入下一新行到pattern中。

+ D:[address[,address]]D 删除multiline pattern中的第一行,不读入下一行。

+ x:交换保持空间和模式空间的内容。

标红的\\n说明,H命令是追加hold spac\\n后,说明前面还有个换行符,虽然最初的保持空间是没有内容,但追加时,在前面自动加个换行(\\n),意思是空行也会加换行符的,所以就多出来一个换行符。

于是我想到可以再把替换的第一个换行符,再替换成空,如下:

[root@study ~]# seq 5 |sed -n H;$x;s/\\n/,/g;s/,//1;p
1,2,3,4,5

注意$x;s/\\n/,/g;s/,//1;p这里的1,是指把前面替换成功的第一个逗号替换成空。

想到这里,其实还有别的方式的​。既然追加前面会有换行符,那我覆盖就没有了吧,可以第一个执行覆盖到保持空间,其它的都追加。与是:

[root@study ~]# seq 5 |sed -n 1h;1!H;$x;s/\\n/,/g;p
1,2,3,4,5

10.

每三个数字加个逗号

[root@study ~]# echo 123456789 |sed -r :a;s/([0-9]+)([0-9]3)/\\1,\\2/;ta
123,456,789

执行第一次时,替换最后一个,跳转后,再对123456匹配替换,直到匹配替换不成功,不执行t命令

11.

查找忽略大小写

[root@study ~]# echo -e "a\\nA\\nb\\nc"|sed  s/a/1/Ig
1
1
b
c

如果只想输出匹配替换成功的行

[root@study ~]# echo -e "a\\nA\\nb\\nc"|sed -n s/a/1/Igp
1
1

12.

换取总行数,“=”打印当前行号

[root@study ~]# seq 10|sed -n $=
10

以上是关于shell三剑客之sed的主要内容,如果未能解决你的问题,请参考以下文章

Shell编程三剑客之sed

shell 三剑客之 sed pattern 详解

linux12shell编程 --> 三剑客之sed命令

shell 脚本——第七节课 三剑客之sed语句

shell高级编程三剑客之sed实践讲解

4.shell编程-文本处理三剑客之sed