标签:Sed
sed - stream editor for filtering and transforming text.
sed,全称stream editor,是一种流编辑器,用于过滤和转换文本。
sed 一次只读取一行文本到缓冲区,然后读取命令,对此行进行编辑,然后读取下一行,重复此过程直到结束。
特点:
因为不需要一次性将文本全部加载出来。
适用场合:
大文件编辑
复杂的编辑命令
单次扫描文件,多个编辑命令
sed 对缓冲区进行编辑,不直接编辑原始文件,所以需要重定向保存更改结果。
命令行方式
sed 脚本方式 (与sed -f
一起使用)
shell 脚本方式
-n
,suppress automatic printing of pattern space #抑制pattern space内容的自动打印,常与p
连用只显示sed动作生效的行(p
的作用是打印patter space
里的内容)
-i
,edit files in place (makes backup if SUFFIX supplied) #直接修改文件,i
后直接加上后缀可以备份源文件,如sed -i.bak commond
,会生成一个.bak的备份文件
-E, -r
,use extended regular expressions in the script #让sed支持扩展正则
-e script
,add the script to the commands to be executed #如果有多段sed命令需要执行,-e
用于间隔每一段sed命令
=
,Print the current line number #打印行号
-f script-file
,add the contents of script-file to the commands to be executed #表示调用 sed 脚本文件
定位文本部分
编辑命令部分
定位文本方法
x
,匹配指定行号
$
,$在正则中表示锚定行尾,在定位部分表示匹配最后一行,即末尾
x,y
,匹配从 x 行到 y 行的内容,如果 y < x,则仅匹配 x
/pattern/
,匹配包含pattern的行
/pattern1/pattern2/
,查询包含pattern1和pattern2的行
/pattern/,x
,从与 pattern 匹配的行到第 x 行间的所有行
x,/pattern/
,从第 x 行到与 pattern 匹配的所有行
x,y!
,匹配不包括 第x行 至 第y行的行
first~step
,匹配从first开始,间隔为step的行
addr1,~N
,匹配从addr1开始的N行,仅GUN sed支持
addr1,+N
,匹配addr1和它后面的N行,仅GUN sed支持
0,addr2
,匹配从开始至addr2的行,仅GUN sed支持
常用编辑命令:
p
,打印pattern space的内容(常与-n一起用,实现只打印匹配行)
a\
,新增, a 的后面可以接字符串,a后面的内容会追加到匹配行的下一行(可以在参数a和要添加的内容之间加上反斜杠\
,能够更清晰的区分命令和要添加的内容)
i\
,插入, 使用方法与a相同,不同的是i后面的内容会插入到匹配行的上一行(同样可以使用\
分隔命令与要添加的内容)
c\
,取代,c后面的字符串可以替换掉匹配的行(可以是n1,n2范围内的多行),可以加\
d
,删除定位行,如/con/d
(删除包含con的行)或 5d
(删除第5行)
r filename
,Append text read from filename.#从r后面的文件中读取文件内容并追加
w filename
,Write the current pattern space to filename.
W filename
,Write the first line of the current pattern space to filename.This is a GNU extension.
&
,&
符号代表的是前面的匹配的模式。
{}
,命令组,组内命令用分号;分隔
sed -n '1p' file
,只输出第一行
sed '1p' file
,会先输出第一行,然后输出全部行(第一行输出两次)
sed -n '3,6p' file
,只输出 3-6 行
-e
,expression,表示将下一个字符串解析为 sed 命令,当只有一个命令时可省略
=
,输出匹配行行号
所以,
sed -n '/Certificate/=' file
只会输出匹配到Certificate的行到行号
sed -n -e '/Certificate/p' -e '/Certificate/=' file
会先执行第一句,输出存在Certificate的行,再(另起一行)输出此行行号
X 注意,sed 不支持多个编辑命令合并使用,如sed -n '/Certificate/p=' file
-f
只有调起 sed 脚本文件时才起作用。追加、插入、修改、删除、替换常常需要几条 sed 命令组合完成,如追加一行的例子可以用 sed '/Certificate/a\a new line.' file
,但如果要追加一个多行文本,就可以定义一个 sed 脚本文件append.sed内容如下:
!/bin/sed -f
/Certificate/a\nA new line.\nAnother new line.
#两个\n是换行符,表示换行
然后执行命令./append.sed file
(执行前需要先执行chmod +x append.sed
给脚本文件添加执行权限)
$
在正则中表示行尾,但在 sed 定位部分表示最后一行。如sed -n '$p' file
表示只输出最后一行
!
符号取反,即x,y!
表示匹配不在 x-y 范围内的行,如sed -n '2,10!p' file
表示输出 2-10 之外的行。但!
不能用于模式匹配,即 不可用,但有一个例外,就是/pattern/!
d
命令,/pattern/!d
可以表示删除匹配行之外的其他行
sed 的编辑命令放在单引号内外皆可,如,sed -n '$p' file
等价于sed -n '$'p file
,但不推荐这样使用
a\
,append,追加a后面的内容到匹配行的下一行(可以使用\
来区分命令和要添加的内容)
如/1$/a\con
(在以1结尾的行的下一行添加con),演示结果如下:
[root@localhost~] #sed -i '/1$/acon' file #\可加可不加
13591641234
13591641231
con
13591641232
13591641235
i\
,insert,用法和\a追加文本一致,只不过是在匹配行的行前添加文本
如2i\con
(在第2行前面插入con),演示结果如下:
[root@localhost~] sed -i '2i\con' file #用法与a相同,\可加可不加。
13591641234
con
13591641231
13591641232
13591641235
c\
,替换匹配的行(可以是n1,n2范围内的多行),用法和上述一致,略
d
,其后不带\
符号,其他与上述类似
sed -i '/vendor/d' file
# 删除包含 “vendor” 的行
sed -i '/IRTCROOM/!d' file
# 删除不包含 “IRTCROOM” 的行,即仅保留包含”IRTCROOM"的行,即反向删除
s/.../.../g
会将匹配到的文本,用新文本进行替换。注意,这里是 文本替换,不是 行替换。
sed -i 's/C:\\Users\\go/D:\\gopath/g' file
#将“C:\Users\go”替换为“D:\gopath”
g
,表示替换所有,不带g时只替换行中的第一处匹配。另外此位置还可以是p
、2
、w
、pg
、2p
p
,它与之前的介绍一致,表示输出行,最好与-n
同时使用
2
,可以是任意数字n,表示替换第 n 个匹配
w
,表示输出到文件,如sed -n 's/seu/nyue/w output' file
#表示在file中将seu替换为nyue,并将替换后的所在行写入output文件
pg
、2p
即两个命令参数的组合
w file
,将指定内容写入文件
sed -n '1,5w output' file
#将file中的1至5行写入output文件
sed -n '/globss/w output' file
#将file中globss所在行写入output文件
r
,如sed '/Centi/r otherfile' file
#在file中匹配Centi的行后追加 otherfile 的内容(与前面的一个例子一致)
q
,在不完全扫描整个文本文件就可以退出
如sed '5 q' file
,即表示扫描前 5 行即退出
y
,与s
一样的用法,如sed 'y/inchars/outchars/' file
{}
命令组括起来的成为一组,其内多个命令用分号分隔。
例如:
sed -n -e '/Certification/p' -e '/Certification/=' file
可写成sed -n '/Certification/{p;=}' file
sed -n '/Certification/{s/i/I/g;s/le/LE/;}' file
#表示在匹配到 Certification 的行中,将 i 全部替换成 I,然后将第一个 le 替换成 LE
编辑命令n
的意义是将匹配行的下一行读取到pattern space中,即匹配当前行的下一行
如sed '/certi/{n;s/11/99/;}' file
即在匹配到certi的行到下一行,使用s/11/99/命令进行编辑
&
,表示被替换的字符串,相当于一个变量用,在模式匹配时可以复用被匹配值如,sed -n 's/seu/(&)/gp' file
等同于sed -n 's/seu/(seu)/gp' file
;
可分隔多个编辑命令
行末少打一个引号'
会进入多行编辑模式
要求:使用seq 12 生成一个序列,最终输出如下结果:
1,2,3,4,5,6,7,8,9,10,11,12
该怎么做呢?首先seq 12的结果是一列数列
1
2
3
4
5
6
7
8
9
10
11
12
那么我就知道我们要做什么了:我们要在每一个数字后面加个,
然后把他们输出到一行上。
当然你完全可以使用xargs把数列输出到一行上,然后使用sed把空格替换为,
就解决了,就像这样seq 12 | xargs | sed -n 's/ /,/gp'
但有的时候我们要面对的是很复杂的数据,这个时候可能需要把整个输入都预读到pattern space
中进行替换处理。那么:a;N;$!ba
这个固定用法正是用来解决这个问题,它可以把整个输入或者文件的内容全部(包括换行符\n
)预读到pattern space
中进行统一处理。用法如下:
sed -n ':a;N;$!ba;s/\n/,/gp'
:a
是一个标签,它通常和ba
是一起使用的,表示一个循环的开始和结束,前面说到,当b
命令遇到一个标签后,会使sed跳转到这个标签的起始位置继续执行。这里a
就是这个标签,:
表示标签的开始,但a
不是固定的,它可以是任意字母或字母组合,只要与b
后面的标签一致即可。
N
命令表示把当前输入的内容追加到pattern space
中。由于sed的动作是从标签a
开始的,当处理到命令b
的时候,获取到b
后面的标签a
,然后就跳转到a
的开始位置继续读取新行并执行命令N
。
$
表示最后一段
!
表示否定
$!ba
就表示最后一行不跳转到标签 a
,也就是只要不是最后一行,每读取完一行都要跳转到 :a
的位置重新开始,只有读取到了最后一行,才继续进行后面的替换命令。所以,整条命令解释过来,就是sed按行进行读取,并且把读取到的每一行都执行N命令添加到pattern space,直到添加完最后一行,才继续执行后面的替换动作。
[root@localhost]# seq 12 | sed -n ':a;N;$!ba;s/\n/,/gp'
1,2,3,4,5,6,7,8,9,10,11,12
与上一个命令类似,不同的是,这个命令适合处理多段文字以空格间隔的场景,实现的效果是以空格为分割,对每段文字进行处理,而不是同时处理全部文字。如下面这个例子是参考某大神提出的:
文本:
Handle 0x0058, DMI type 20, 19 bytes
Memory Device Mapped Address
Starting Address: 0x0001FFFFC00
Ending Address: 0x0001FFFFFFF
Range Size: 1 kB
Physical Device Handle: 0x0047
Memory Array Mapped Address Handle: 0x004B
Partition Row Position: Unknown
Interleave Position: Unknown
Interleaved Data Depth: Unknown
sample 0x0058, DMI type 20, 19 bytes
Memory Device Mapped Address
Starting Address: 0x0001FFFFC00
Ending Address: 0x0001FFFFFFF
Range Size: 2 kB
Physical Device Handle: 0x0047
Memory Array Mapped Address Handle: 0x004B
Partition Row Position: Unknown
Interleave Position: Unknown
Interleaved Data Depth: Unknown
Handle 0x0058, DMI type 20, 19 bytes
Memory Device Mapped Address
Starting Address: 0x0001FFFFC00
Ending Address: 0x0001FFFFFFF
Range Size: 3 kB
Physical Device Handle: 0x0047
Memory Array Mapped Address Handle: 0x004B
Partition Row Position: Unknown
Interleave Position: Unknown
Interleaved Data Depth: Unknown
要求:每个段落是Handle开头的就取出Range Size的值:
1 kB
3 kB
sed -n '/^Handle/{:a;N;/\n$/!{$!ba};s/.*Range Size: \([^\n]*\).*/\1/p}' file
[解析]
文本就3个段落,2个空行为分割,首先想到肯定是以空行为分割,把一整段文本读取在一起,然后统一进行匹配和替换。特别注意N
读取内容匹配空行是 /\n$/
,而不是一般的 /^$/
。因此,我们可以使用sed的段落处理命令 :a;N;$!ba
来处理这个问题,而在这个场景下,我们使用的是 :a;N;/\n$/!{$!ba}
,以空行分割按段预读处理。
为什么要用 /\n$/!{$!ba}
而不是直接用 /\n$/!ba
?
笔者曾在实验的时候曾纠结过这个问题,原因是给定的文本中,最后一行不是空行,因此当sed处理到最后一行时,由于没有 \n$
来匹配 /\n$/!ba
,因此sed仍然会执行 ba
从而跳转到开始来执行 N
,当sed发现没有下一行可以追加了,就会结束动作而不再进行后续的替换操作。因此除非最后一行是空行,否则使用 /\n$/!ba
会导致最终的处理结果不包含最后一个空行之后的内容。
因此,我们使用 /\n$/!{$!ba}
,保证sed在处理到最后一行的时候,能够不再继续跳转到 N;
而是继续后面的替换动作。
还有另外的一个例子大家可以去这个大神的文章《sed之h;H和:a;N;ba使用精解(对段落进行操作》去看一下,不再多讲。
https://www.jianshu.com/p/c45788346b26
http://blog.chinaunix.net/uid-10540984-id-2983419.html