胸怀万里世界, 放眼无限未来。这篇文章主要讲述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.
地址
描述
【shell三剑客之sed】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-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
isnetserv48128/tcp# Image Systems Network Services
isnetserv48128/udp# Image Systems Network Services
blp548129/tcp# Bloomberg locator
blp548129/udp# Bloomberg locator
com-bardac-dw48556/tcp# com-bardac-dw
com-bardac-dw48556/udp# com-bardac-dw
iqobject48619/tcp# iqobject
iqobject48619/udp# iqobject
matahari49000/tcp# Matahari Broker
对比不加-n和加-n
[root@study ~]# tail /etc/services|sed/^blp5/p
3gpp-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
isnetserv48128/tcp# Image Systems Network Services
isnetserv48128/udp# Image Systems Network Services
blp548129/tcp# Bloomberg locator
blp548129/tcp# Bloomberg locator
blp548129/udp# Bloomberg locator
blp548129/udp# Bloomberg locator
com-bardac-dw48556/tcp# com-bardac-dw
com-bardac-dw48556/udp# com-bardac-dw
iqobject48619/tcp# iqobject
iqobject48619/udp# iqobject
matahari49000/tcp# Matahari Broker
[root@study ~]# tail /etc/services|sed -n/^blp5/p
blp548129/tcp# Bloomberg locator
blp548129/udp# Bloomberg locator
打印第一行
[root@study ~]# tail /etc/services|sed -n1p
3gpp-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
只打印奇数行
[root@study ~]# tail /etc/services|sed -n1~2p
3gpp-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
isnetserv48128/udp# Image Systems Network Services
blp548129/udp# Bloomberg locator
com-bardac-dw48556/udp# com-bardac-dw
iqobject48619/udp# iqobject
打印匹配行,及以后一行
[root@study ~]# tail /etc/services|sed -n/^blp5/,+1p
blp548129/tcp# Bloomberg locator
blp548129/udp# Bloomberg locator
不打印最后一行,感叹号是取反
[root@study ~]# tail /etc/services|sed -n$!p
3gpp-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
isnetserv48128/tcp# Image Systems Network Services
isnetserv48128/udp# Image Systems Network Services
blp548129/tcp# Bloomberg locator
blp548129/udp# Bloomberg locator
com-bardac-dw48556/tcp# com-bardac-dw
com-bardac-dw48556/udp# com-bardac-dw
iqobject48619/tcp# iqobject
iqobject48619/udp# iqobject
引用变量,下面两种写法效果一样的
[root@study ~]# a=1
[root@study ~]# tail /etc/services|sed -n$a,3p
3gpp-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
isnetserv48128/tcp# Image Systems Network Services
isnetserv48128/udp# Image Systems Network Services
[root@study ~]# tail /etc/services|sed -n "$a,3p"
3gpp-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
isnetserv48128/tcp# Image Systems Network Services
isnetserv48128/udp# Image Systems Network Services
删除是d,打印是把匹配的打印出来,删除是把匹配的删除,删除只是不再用-n选项
5.
替换
替换开头是blp5的字符串,并打印
[root@study ~]# tail /etc/services |sed -n s/^blp5/test/p
test48129/tcp# Bloomberg locator
test48129/udp# Bloomberg locator
使用& 命令引用匹配内容并替换
[root@study ~]# tail /etc/services |seds/48129/& .0/
3gpp-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
isnetserv48128/tcp# Image Systems Network Services
isnetserv48128/udp# Image Systems Network Services
blp548129.0/tcp# Bloomberg locator
blp548129.0/udp# Bloomberg locator
com-bardac-dw48556/tcp# com-bardac-dw
com-bardac-dw48556/udp# com-bardac-dw
iqobject48619/tcp# iqobject
iqobject48619/udp# iqobject
matahari49000/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-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
isnetserv48128/tcp# Image Systems Network Services
isnetserv48128/udp# Image Systems Network Services
test48129/tcp# Bloomberg locator
test48129/udp# Bloomberg locator
com-bardac-dw48556/tcp# com-bardac-dw
com-bardac-dw48556/udp# com-bardac-dw
iqobject48619/tcp# iqobject
iqobject48619/udp# iqobject
matahari49000/tcp# Matahari Broker
注,上面的还可以写成
[root@study ~]# tail /etc/services |sed -e s/blp5/test/ -e s/3g/4g/
4gpp-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
isnetserv48128/tcp# Image Systems Network Services
isnetserv48128/udp# Image Systems Network Services
test48129/tcp# Bloomberg locator
test48129/udp# Bloomberg locator
com-bardac-dw48556/tcp# com-bardac-dw
com-bardac-dw48556/udp# com-bardac-dw
iqobject48619/tcp# iqobject
iqobject48619/udp# iqobject
matahari49000/tcp# Matahari Broker
分组使用,加上test
[root@study ~]# tail /etc/services |sed -r s/(.*)(.*)(#.*)/\\1\\2test \\3/
3gpp-cbsp48049/tcptest # 3GPP Cell Broadcast Service Protocol
isnetserv48128/tcptest # Image Systems Network Services
isnetserv48128/udptest # Image Systems Network Services
blp548129/tcptest # Bloomberg locator
blp548129/udptest # Bloomberg locator
com-bardac-dw48556/tcptest # com-bardac-dw
com-bardac-dw48556/udptest # com-bardac-dw
iqobject48619/tcptest # iqobject
iqobject48619/udptest # iqobject
matahari49000/tcptest # Matahari Broker
将协议与端口号位置调换
[root@study ~]# tail /etc/services |sed -r s/(.*)(\\< [0-9]+)\\/(tcp|udp)(.*)/\\1\\3\\/\\2\\4/
3gpp-cbsptcp/48049# 3GPP Cell Broadcast Service Protocol
isnetservtcp/48128# Image Systems Network Services
isnetservudp/48128# Image Systems Network Services
blp5tcp/48129# Bloomberg locator
blp5udp/48129# Bloomberg locator
com-bardac-dwtcp/48556# com-bardac-dw
com-bardac-dwudp/48556# com-bardac-dw
iqobjecttcp/48619# iqobject
iqobjectudp/48619# iqobject
mataharitcp/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 ~]# echo1 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-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
isnetserv48128/tcp# Image Systems Network Services
isnetserv48128/udp# Image Systems Network Services
test
blp548129/tcp# Bloomberg locator
test
blp548129/udp# Bloomberg locator
com-bardac-dw48556/tcp# com-bardac-dw
com-bardac-dw48556/udp# com-bardac-dw
iqobject48619/tcp# iqobject
iqobject48619/udp# iqobject
matahari49000/tcp# Matahari Broker
替换blp5替换新行
[root@study ~]# tail /etc/services |sed /blp5/c \\test
3gpp-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
isnetserv48128/tcp# Image Systems Network Services
isnetserv48128/udp# Image Systems Network Services
test
test
com-bardac-dw48556/tcp# com-bardac-dw
com-bardac-dw48556/udp# com-bardac-dw
iqobject48619/tcp# iqobject
iqobject48619/udp# iqobject
matahari49000/tcp# Matahari Broker
在指定行下一行添加一行
[root@study ~]# tail -5 /etc/services |sed 2a \\test
com-bardac-dw48556/tcp# com-bardac-dw
com-bardac-dw48556/udp# com-bardac-dw
test
iqobject48619/tcp# iqobject
iqobject48619/udp# iqobject
matahari49000/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-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
isnetserv48128/tcp# Image Systems Network Services
isnetserv48128/udp# Image Systems Network Services
blp548129/tcp# Bloomberg locator
123
456
blp548129/udp# Bloomberg locator
123
456
com-bardac-dw48556/tcp# com-bardac-dw
com-bardac-dw48556/udp# com-bardac-dw
iqobject48619/tcp# iqobject
iqobject48619/udp# iqobject
matahari49000/tcp# Matahari Broker
将匹配行写入文件
[root@study ~]# tail /etc/services |sed /blp5/w b.txt
3gpp-cbsp48049/tcp# 3GPP Cell Broadcast Service Protocol
isnetserv48128/tcp# Image Systems Network Services
isnetserv48128/udp# Image Systems Network Services
blp548129/tcp# Bloomberg locator
blp548129/udp# Bloomberg locator
com-bardac-dw48556/tcp# com-bardac-dw
com-bardac-dw48556/udp# com-bardac-dw
iqobject48619/tcp# iqobject
iqobject48619/udp# iqobject
matahari49000/tcp# Matahari Broker
[root@study ~]# cat b.txt
blp548129/tcp# Bloomberg locator
blp548129/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|sedN; 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|sedN; 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"|seds/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
推荐阅读
- 你要允许此应用对你的设备进行更改吗()
- 生成文件夹权限设置的账户列表(icacls 命令)
- oeasy教您玩转python - 003 - #- 继续运行
- Spark+ES+ClickHouse 构建DMP用户画像
- 使用nginx的auth_basic实现prometheus 安全认证
- 2022最新网易云音乐代挂源码[含教程]自动刷歌签到
- 前端主流布局系统进阶与实战,轻松解决页面布局难题
- lvm逻辑卷管理 创建 |ext4 xfs扩容
- 轻松实现Rust系统入门,实战编译器开发