24. 文本处理工具之sed
- Date:
2018-09-02
24.1. sed简介
sed是linux文本处理工具的三件套之一。
- sed介绍
stream editor for filtering and transforming text,sed是流编辑器。一般sed更擅长文本的行操作。
sed 全名为 stream editor,流编辑器,用程序的方式来编辑文本,功能相当的强大。是贝尔实验室的 Lee E.McMahon 在 1973 年到 1974 年之间开发完成,目前可以在大多数操作系统中使用,sed 的出现作为 grep 的继任者。与vim等编辑器不同,sed 是一种非交互式编辑器(即用户不必参与编辑过程),它使用预先设定好的编辑指令对输入的文本进行编辑,完成之后再输出编辑结构。sed 基本上就是在玩正则模式匹配,
- sed工作原理
sed会一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,成为”模式空间”,接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。
24.2. sed详解
24.2.1. 命令格式
sed [选项]… {脚本(如果没有其他脚本)} [输入文件]…
24.2.2. 命令参数
注意
- 重点常用参数:
-n结合子命令p-i用来处理文本内容替换。用这个参数才能替换文件中指定内容并写入文件。
参数
参数 |
含义作用 |
-n |
只打印模式匹配的行 |
-e |
直接在命令行模式上进行sed动作编辑,此为默认选项 |
-f |
将sed的动作写在一个文件内,用–f filename 执行filename内的sed动作 |
-r |
支持扩展表达式 |
-i |
直接修改文件内容 |
子命令
命令 |
作用 |
a |
在当前行下面插入文本。 |
i |
在当前行上面插入文本。 |
c |
把选定的行改为新的文本。 |
d |
删除,删除选择的行。 |
D |
删除模板块的第一行。 |
s |
替换指定字符 |
h |
拷贝模板块的内容到内存中的缓冲区。 |
H |
追加模板块的内容到内存中的缓冲区。 |
g |
获得内存缓冲区的内容,并替代当前模板块中的文本。 |
G |
获得内存缓冲区的内容,并追加到当前模板块文本的后面。 |
l |
列表不能打印字符的清单。 |
n |
读取下一个输入行,用下一个命令处理新的行而不是用第一个命令。 |
N |
追加下一个输入行到模板块后面并在二者间嵌入一个新行,改变当前行号码。 |
p |
打印模板块的行。 |
P(大写) |
打印模板块的第一行。 |
q |
退出Sed。 |
b |
lable分支到脚本中带有标记的地方,如果分支不存在则分支到脚本的末尾。 |
r |
file从file中读行。 |
t |
label if分支,从最后一行开始,条件一旦满足或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。 |
T |
label错误分支,从最后一行开始,一旦发生错误或者T,t命令,将导致分支到带有标号的命令处,或者到脚本的末尾。 |
w |
file写并追加模板块到file末尾。 |
W |
file写并追加模板块的第一行到file末尾。 |
! |
表示后面的命令对所有没有被选定的行发生作用。 |
= |
打印当前行号码。 |
# |
把注释扩展到下一个换行符以前。 |
替换标记
g |
表示行内全面替换。 |
p |
表示打印行。 |
w |
表示把行写入一个文件。 |
x |
表示互换模板块中的文本和缓冲区中的文本。 |
y |
表示把一个字符翻译为另外的字符(但是不用于正则表达式) |
1 |
子串匹配标记 |
& |
已匹配字符串标记 |
24.3. sed实例
24.3.1. 参数实例
参数
-n
1[root@zzjlogin ~]# echo -e 'hello world\nnihao' | sed 's/hello/A/'
2A world
3nihao
4[root@zzjlogin ~]# echo -e 'hello world\nnihao' | sed -n 's/hello/A/'
5[root@zzjlogin ~]# echo -e 'hello world\nnihao' | sed -n 's/hello/A/p'
6A world
小技巧
参数 -n 是只打印匹配的行,后面是替换操作,
所以 s 和 p 是替换命令,如果没有替换后的打印命令 p 也是不能打印的。
参数
-e
使用参数 -e 可以在多次处理,而不用每次处理结果用管道连接后再处理。
参考下面实例:
1[root@zzjlogin ~]# echo -e 'hello world' | sed -e 's/hello/A/' -e 's/world/B/'
2A B
3[root@zzjlogin ~]# echo -e 'hello world' | sed 's/hello/A/;s/world/B/'
4A B
参数
-i
sed默认会把输入行读取到模式空间,简单理解就是一个内存缓冲区,sed子命令处理的内容是模式空间中的内容, 而非直接处理文件内容。因此在sed修改模式空间内容之后,并非直接写入修改输入文件,而是打印输出到标准输出。 如果需要修改输入文件,那么就可以指定-i选项。
注意
参数 -i 替换文件内容时,替换标记命令的 p 不可以一起用,否则会出现替换文件中有两行。
1[root@zzjlogin ~]# cat test.txt
2hello world
3[root@zzjlogin ~]# sed 's/hello/A/' test.txt
4A world
5[root@zzjlogin ~]# cat test.txt
6hello world
7[root@zzjlogin ~]# sed -i 's/hello/A/' test.txt
8[root@zzjlogin ~]# cat test.txt
9A world
小技巧
默认匹配替换每行第一次匹配的地方,如果把文件中所有匹配的都替换,需要用替换标记命令 g
1[root@zzjlogin ~]# cat test.txt
2hello world
3hello world
4hello world
5hello world hello
6
7[root@zzjlogin ~]# sed -i 's/hello/A/' test.txt
8[root@zzjlogin ~]# cat test.txt
9A world
10A world
11A world
12A world hello
13
14[root@zzjlogin ~]# sed -i 's/A/hello/' test.txt
15[root@zzjlogin ~]# cat test.txt
16hello world
17hello world
18hello world
19hello world hello
20
21[root@zzjlogin ~]# sed -i 's/hello/A/g' test.txt
22[root@zzjlogin ~]# cat test.txt
23A world
24A world
25A world
26A world A
参数
-r
sed命令的匹配模式支持正则表达式的,默认只能支持基本正则表达式,如果需要支持扩展正则表达式,那么需要添加-r选项。
24.3.2. 行选择实例
默认情况下sed会对每一行内容进行匹配、处理、输出,某些情况不需要对处理的文本全部编辑,只需要其中的一部分,比如1-10行,偶数行,或者是包含”hello”字符串的行,这种情况下就需要我们去定位特定的行来处理,而不是全部内容。
只显示指定行
小技巧
文件末行用 $ 来表示。
显示指定行:
将第4行中hello字符串替换为A,其它行如果有hello也不会被替换。
1sed –n '4s/hello/A/' message
显示末行:
$符号表示最后一行,和正则中的$符号类似,但是第1行不用^表示,直接1就行了。
1sed –n '$s/hello/A/' message
显示指定区间的行内容
将第2-4行中hello字符串替换为A,其它行如果有hello也不会被替换。
1sed –n '2,4s/hello/A/' message
从第2行开始,再接着往下数4行,也就是2-6行,这些行会把hello字符替换为A。
1sed –n '2,+4s/hello/A/' message
第4行开始,到第6行。解释6的由来,”4,~3”表示从4行开始到下一个3的倍数,这里从4开始算,那就是6了,当然9就不是了,因为是要求3的第一个超过前边数字4的倍数,感觉这种适用场景不会太多。
1sed –n '4,~3s/hello/A/' message
按照等差数列取行
从第4行开始,每隔3行就把hello替换为A。比如从4行开始,7行,10行等依次+3行。这个比较常用,比如3替换为2的时候,也就是每隔2行的步调,可以实现奇数和偶数行的操作。
1sed –n '4~3s/hello/A/' message
选择去除指定行的其余行
!符号表示取反,该命令是将除了第1行,其它行hello替换为A,上述定址方式也可以使用!符号。
1sed -n '1!s/hello/A/' message
空行操作
删除空行
1sed -n '/^$/d' message
备注
上面命令中 d 是替换标识命令,即删除操作。
24.3.3. 子命令实例
子命令
a
a表示在指定行下边插入指定行的内容。
将message文件中每一行下边都插入添加一行内容是A。
1sed 'a A' message
将message文件中1-2行的下边插入添加一行内容是A
1sed '1,2a A' message
在匹配内容的行下面追加一行内容:
备注
这是每个匹配行下面都追加。
1[root@zzjlogin ~]# cat /root/test.txt
290
391
492
593
694
795
896
997
1098
1199
12100
1390
1491
1592
1693
1794
1895
1996
2097
2198
2299
23100
24[root@zzjlogin ~]# sed -i '/91/a\append' /root/test.txt
25[root@zzjlogin ~]# cat /root/test.txt
2690
2791
28append
2992
3093
3194
3295
3396
3497
3598
3699
37100
3890
3991
40append
4192
4293
4394
4495
4596
4697
4798
4899
49100
子命令
i
i和a使用上基本上一样,只不过是在指定行上边插入指定行的内容。
将message文件中每一行上边都插入添加一行内容是A。
1sed 'i A' message
将message文件中1-2行的上边分别添加3行,3行内容分别是A、B、C,这里使用了n,插入多行内容都可以按照这种方式来实现。
1sed '1,2i A\nB\nC' message
子命令
c
c是表示把指定的行内容替换为自己需要的行内容。
将message文件中所有的行内容都分别替换为A行内容。
1sed 'c A' message
将message文件中1-2行的内容替换为A,注意这里说的是将1-2行所有的内容只替换为一个A内容,也就是1-2行内容编程了一行,定址如果连续就是这种情况。
1sed '1,2c A' message
子命令
d
d表示删除指定的行内容,比较简单,更容易理解。
将message所有行全部删除
1sed 'd' message
将message文件中1-3行内容删除。
1sed '1,3d' message
子命令
y
y表示字符替换,可以替换多个字符,只能替换字符不能替换字符串,且不支持正则表达式,具体使用方法看例子。
把message中所有a字符替换为A符号,所有b字符替换为B符号。
1sed 'y/ab/AB/' message
子命令
=
= 可以将行号打印出来。
将指定行的上边显示行号。
1sed '1,2=' message
2
31
4nihao
52
6hello world
子命令
r
r,类似于a,也是将内容追加到指定行的后边,只不过r是将指定文件内容读取并追加到指定行下边。
将a.txt文件内容读取并插入到message文件第2行的下边。
1sed '2r a.txt' message
子命令
s
s为替换子命令,是平时sed使用的最多的子命令,没有之一。因为支持正则表达式,功能变得强大无比,下边来详细地说说子命令s的使用方法。
- 基本语法:
[address]s/pattern/replacement/flags
上面的每个单词含义:
- replacement部分用下列字符会有特殊含义
&:用正则表达式匹配的内容进行替换 n:回调参数 ():保存被匹配的字符以备反向引用n时使用,最多9个标签,标签书序从左到右
- flags
n:可以是1-512,表示第n次出现的情况进行替换 g:全局更改 p:打印模式空间的内容 w file:写入到一个文件file中
在匹配结果前后分别加了111、222。
1cat message
2hello 123 world
3
4sed -r 's/([a-z]+)( [0-9]+ )([a-z]+)/111&222/' message
其他子命令
把message文件中内容的每行第一个字符i替换为A,然后把修改内容另存为b.txt文件。
1sed -n 's/i/A/w b.txt' message
把message文件中每行的第2个i字符替换为A。
1sed 's/i/A/2' message
在message文件中每行的首尾分别加上111、222。
1sed -r 's/.*/111&222/' message
24.3.4. 替换标识命令实例
把文件从第22行到第33行复制到56行后面。
1sed '22h;23,33H;56G' /etc/passwd
把文件从第22行到第33行移动到第56行后面。
1sed '22{h;d};23,33{H;d};56g' /etc/passwd
只显示每行的第一个单词。
1sed -r 's/([a-Z]+)([^a-Z]+)(.*)/\1/' /etc/passwd
删除第5行。
1sed '4{n;d}' message
交换每行的第一个单词和最后一个单词。
- 思路:
文件
/etc/passwd的列数是固定的。所以可以用分组,然后这些分组后向引用。这样就达到了要求的目的。
1sed -r 's/([a-Z]+)([^a-Z]+)(.*)([^a-Z]+)([a-Z]+)([^a-Z]*$)/\5\2\3\4\1\6/' /etc/passwd