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
每行后添加新行
[root@study ~]# seq 5|sed G
1
2
3
4
5
打印匹配行的上一行内容
[root@study ~]# seq 5|sed -n /3/x;p;h
2
其实执行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的主要内容,如果未能解决你的问题,请参考以下文章