Sed是一个流编辑器。流编辑器用于在输入流(文件或管道输入)上执行基本文本转换。虽然在某些方面类似于允许脚本化编辑(如ed)的编辑器,但sed的工作原理是只对输入进行一次传递,因此效率更高。
语法:
sed OPTIONS… [SCRIPTS] [INPUTFILE…]
sed每次从INPUTFILE中读取一行记录,并在该记录上执行SCRIPT。
sed 首先从 INPUTFILE 中读取第一行,然后执行所有的SCRIPT;再读取第二行,执行所有 SCRIPT,重复这个过程,直到INPUTFILE 结束。
通过指定[OPTIONS] 还可以给 sed 传递一些可选的选项。
注意:
sed 绝不会修改原始文件 INPUTFILE,它只是将结果内容输出到标准输出设备。如果要保持变更,应该使用重定向 > filename.txt
OPTIONS概述:
-n,–quiet,–silent:抑制模式空间的自动打印
-e script,–expression=script:将脚本添加到要执行的命令中
-f script-file,–file=script-file:将脚本文件的内容添加到要执行的命令中
-i[SUFFIX],–in-place[=SUFFIX]:直接修改读取的文件内容,而不是由屏幕输出
-r,–regexp-extended:在脚本中使用扩展正则表达式。
-c, –copy:该选项应和-i 配合使用。使用-i 时,通常在命令执行完成后,sed 使用临时文件来保持更改 后的内容,然后把该临时文件重命名为输入文件。但这样会改变文件的所有者。配合c选项,可以保持文件所有者不变。也可以使 用–copy 来代替。
-l N, –line-length=N:指定行的长度,需要和 l 命令配合使用(注意选项 l 和命令 l,不要弄混了),使用-l 选项即指定行的长度。也可以使用–line-length 来代替.
SCRIPTS概述
sed程序由一个或多个sed commands组成,这些命令由一个或多个-e、-f、–expression和–file选项传递。
Sed commands遵循以下语法:
[addr]X[options]
X是一个单字母sed command。[addr]是一个可选的行地址。如果指定[addr],则X命令只对匹配的行执行。[addr]可以是一个单行号、一个正则表达式或一个行范围(参见下面的地址范围)。额外的 [options] 用于某些 sed commands。
下面的示例表示删除输入中的第30行到第35行。30、35是一个地址范围。d为delete命令:
sed ‘30,35d’ input.txt > output.txt
commands概述
GNU sed支持以下命令。一些是标准POSIX命令,而另一些是GNU扩展。
i text:在一行之前插入文本。
a text :在一行之后追加文本。
c text :用文本替换(更改)行。
d:删除模式空间。开始下一个循环。
e:执行在模式空间中找到的命令,并用输出替换模式空间;末尾换行符被抑制。
e command:执行命令并将其输出发送到输出流。该命令可以跨多行运行,但最后一行以反斜杠结尾。
F:(filename)打印当前输入文件的文件名(带一个换行符)。
g:将模式空间的内容替换为保持空间的内容。
G:复制/追加保持空间到模式空间。
h:(hold)用模式空间的内容替换保持空间的内容。
H:将换行符追加到保持空间的内容,然后将模式空间的内容追加到保持空间的内容。
l(小写的L):打印不可见字符,例如制表符\t,行尾标志$等,如果在l后面指定了数字,那么会在第n个字符处使用一个不可见自动折行。
n,N:将下一行输入读取/追加到模式空间。
p:打印模式空间的内容
P:输出到当前模式空间的第一个嵌入的换行符为止。
r filename :读取文件的文件名。
R filename:在当前周期结束或读取下一个输入行时,将要读取的文件名行放入队列并插入到输出流中。
s/regexp/replacement/[flags]:根据模式空间的内容匹配regexp。如果找到,用replacement替换匹配的字符串。
[flags]:
- g:对regexp的所有匹配项应用替换,而不仅仅是第一个。
- number:只替换regexp的第number个匹配项。
- p:如果进行了替换,则打印新的模式空间。
- w filename:如果进行了替换,则将结果写入指定的文件
- e:代表执行,该标志可以将模式空间中的任何内容都当做shell来执行,并把命令执行的结果返回到模式空间,该标志只有GNU Sed中才可使用
- I,i:正则表达式匹配的I修饰符是一个GNU扩展,它使sed以不区分大小写的方式匹配regexp。
t label:(test)只在读取最后一个输入行或进行条件转移后,有成功的替换时才进行转移。这个标签可以省略,这样下一个循环就开始了。
w filename:将模式空间写入文件名
W filename:将模式空间中直到第一个换行符为止的部分写入给定的文件名
x:交换保留空间和模式空间的内容。
y /src/dst/:将模式空间中与源字符匹配的任何字符与dest字符中的相应字符进行音译
z(zap)该命令清空模式空间的内容。
#注释,直到下一个换行。
{cmd;cmd……}:将几个命令组合在一起。
=:输出当前输入行号(后跟换行符)。
:label 指定分支命令的标签位置(b, t, t)。
地址范围
Sed命令可以不指定地址,在这种情况下,命令将对所有输入行执行;
只有一个地址,在这种情况下,该命令只会对匹配该地址的输入行执行;
或者使用两个地址,在这种情况下,命令将对所有匹配从第一个地址开始并继续到第二个地址的行执行。
关于地址范围有三件事要注意:语法是addr1,addr2(即,地址用逗号分隔);addr1匹配的行总是被接受,即使addr2选择了更早的行;如果addr2是一个regexp,它将不会针对addr1匹配的行进行测试。
在地址(或地址范围)后面,在命令前面,加一个!可以插入,它指定只在地址(或地址范围)不匹配的情况下执行命令。
支持以下地址类型:
number:只匹配指定的行号。
first~step:匹配每一步从一行开始。例如,“sed -n 1~2p”将输出输入流中的所有奇数行,地址2~5将从第2行开始每5行匹配一次。首先可以是零;在本例中,sed的操作就像它等于step一样。(这是分机。)
$:匹配最后一行。
/regexp/:匹配符合正则表达式regexp的行。
0,addr2:以“匹配第一个地址”的状态开始,直到找到addr2。这类似于1,addr2,除了如果addr2匹配输入的第一行0,addr2形式将在其范围的结束,而1,addr2形式仍将在其范围的开始。这只在addr2是正则表达式时有效。
addr1,+N:将匹配addr1和addr1后面的N行。
addr1,~N:将匹配addr1和addr1后面的行,直到下一行的输入行号是N的倍数。
模式空间
Sed 有两个内置的存储空间:
- 模式空间:如你所知,模式空间用于 sed 执行的正常流程中。该空间 sed 内置的一个缓冲区,用于存放,修改从输入文件读取的内容
- 保持空间:保持空间是另外一个缓冲区,用来存放临时数据。Sed 可以在保持空间 和模式空间交换数据,但是不能在保持空间上执行普通的 sed 命令。我们已经讨论 过,每次循环读取数据过程中,模式空间的内容都会被清空,然而保持空间的内容 则保持不变,不会在循环中被删除。
退出状态
- 0:成功完成。
- 1:无效命令、无效语法、无效正则表达式或与——posix一起使用的GNU sed扩展命令。
- 2:命令行中指定的一个或多个输入文件无法打开(例如,如果没有找到文件,或读取权限被拒绝)。继续处理其他文件。
- 4:在运行时出现I/O错误或严重的处理错误,GNU sed将立即中止。
执行流程
Sed 脚本执行遵从下面简单易记的顺序:Read,Execute,Print,Repeat(读取,执行,打印,重复),简称 REPR
分析脚本执行顺序:
- 读取一行到模式空间(sed 内部的一个临时缓存,用于存放读取到的内容)
- 在模式空间中执行命令。如果使用了{ } 或 –e 指定了多个命令,sed 将依次执行每 个命令
- 打印模式空间的内容,然后清空模式空间
- 重复上述过程,直到文件结束
下面我们正式开始
这是我们的测试文件
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# cat /tmp/student.txt
1 zhangsan 男 20
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
查找:
1:查看第一行
[root@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘1 p’ /tmp/student.txt
1 zhangsan 男 20
1 zhangsan 男 20
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
#我们发现后面的几行也显示出来了,并且第1行显示了两次,那么怎么解决呢?我们加一个-n选项就可以了
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n ‘1 p’ /tmp/student.txt
1 zhangsan 男 20
2:从第2行开始,查看到最后一行
[root@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n ‘2,$p’ /tmp/student.txt
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
3:只查看奇数行
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n ‘1~2p’ /tmp/student.txt
1 zhangsan 男 20
3 wangwu 男 22
5 zhaogang 男 22
4:只查看偶数行
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n ‘2~2p’ /tmp/student.txt
2 lisi 男 21
4 zhaoliu 女 23
6 lihong 女 22
5:查看匹配zhao的行
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n ‘/zhao/p’ /tmp/student.txt
4 zhaoliu 女 23
5 zhaogang 男 22
6:查看匹配zhang或zhao的行
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n ‘/zhang|zhao/p’ /tmp/student.txt
1 zhangsan 男 20
4 zhaoliu 女 23
5 zhaogang 男 22
7:查看1开头的行
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n ‘/^1/p’ /tmp/student.txt
1 zhangsan 男 20
8:查看以22结尾的的有哪些行
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n ‘/22$/p’ /tmp/student.txt
3 wangwu 男 22
5 zhaogang 男 22
6 lihong 女 22
9:查看1,3,5开头的行
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n ‘/^[135]/p’ /tmp/student.txt
1 zhangsan 男 20
3 wangwu 男 22
5 zhaogang 男 22
10:查看匹配wangwu后面的2行
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n ‘/wangwu/,+2p’ /tmp/student.txt
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
11:查看从第一次匹配wangwu的行到第4行
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n ‘/wangwu/,4p’ /tmp/student.txt
3 wangwu 男 22
4 zhaoliu 女 23
12:打印匹配zhangssan的行开始到匹配zaholiu的行之间的所有内容
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n ‘/zhangsan/,/zhaoliu/ p’ /tmp/student.txt
1 zhangsan 男 20
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
替换:
1:将zhangsan换成ZHANGSAN
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘s/zhangsan/ZHANGSAN/’ /tmp/student.txt
1 ZHANGSAN 男 20
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
2:只把匹配zhangsan的行的a替换成大写A
[root@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘/zhangsan/s/a/A/’ /tmp/student.txt
1 zhAngsan 男 20
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
#我们发现zhangsan的第一个a替换成了大写A,第二个没有替换,如果要全部替换,就需要加上-g选项
[root@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘/zhangsan/s/a/A/g’ /tmp/student.txt
1 zhAngsAn 男 20
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
3:匹配zhangsan的行的第二个a替换成大写A
[root@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘/zhangsan/s/a/A/2’ /tmp/student.txt
1 zhangsAn 男 20
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
替换命令分界符:
上面的所有例子中,我们都是使用了sed的默认分界符/。如果在regexp或replacement中有/,那么需要使用反斜杠\来转移,下面我们来一个例子:
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ wwwroot]# cat path.txt
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
1:我们将/root/bin替换成/zhangsan/bin
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ wwwroot]# sed ‘s/\/root\/bin/\/zhangsan\/bin/’ path.txt
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/zhangsan/bin
每个/前面都使用\转义,会显得很混乱。幸 运的是,你可以使用任何一个字符作为 sed 替换命令的分界符,如 | 或 ^ 或 @ 或者 !
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ wwwroot]# sed ‘s!/root/bin/!/zhangsan/bin!’ path.txt
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ wwwroot]# sed ‘s@/root/bin/@/zhangsan/bin@’ path.txt
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
&的作用——获取匹配到的模式:
当在 replacement-string 中使用&时,它会被替换成匹配到的 original-string 或正则表达式。
1:当每行开头的数字需要加上[]
[root@iZ2ze9v82ax0ox0j12ahfhZ wwwroot]# sed “s/^[1-9]/[&]/g” /tmp/student.txt
[1] zhangsan 男 20
[2] lisi 男 21
[3] wangwu 男 22
[4] zhaoliu 女 23
[5] zhaogang 男 22
[6] lihong 女 22
2:1:当每行开头的数字需要加上0
[root@iZ2ze9v82ax0ox0j12ahfhZ wwwroot]# sed “s/^[1-9]/0&/g” /tmp/student.txt
01 zhangsan 男 20
02 lisi 男 21
03 wangwu 男 22
04 zhaoliu 女 23
05 zhaogang 男 22
06 lihong 女 22
删除
1:删除第1行
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘1 d’ /tmp/student.txt
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
2:删除第1行到第3行
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘1,3 d’ /tmp/student.txt
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
3:删除匹配zhao的行
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘/zhao/ d’ /tmp/student.txt
1 zhangsan 男 20
2 lisi 男 21
3 wangwu 男 22
6 lihong 女 22
4:删除1开头的行
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘/^1/ d’ /tmp/student.txt
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
追加
语法:sed ‘[address] a the-line-to-append’ input-file
1:在匹配zhansan的行后面追加
[root@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘/zhangsan/a 1.1 wanglun 男 18’ /tmp/student.txt
1 zhangsan 男 20
1.1 wanglun 男 18
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
2:在第2行后面追加
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘2a 1.1 wanglun 男 18’ /tmp/student.txt
1 zhangsan 男 20
2 lisi 男 21
1.1 wanglun 男 18
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
3:在结尾追加
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘$a 1.1 wanglun 男 18’ /tmp/student.txt
1 zhangsan 男 20
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
1.1 wanglun 男 18
插入
语法:sed ‘[address] i the-line-to-insert’ input-file
插入命令 insert 命令和追加命令类似,只不过是在指定位置之前插入行
修改
语法:sed ‘[address] c the-line-to-insert’ input-file
1:换掉最后一行
[root@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘$c 1.1 wanglun 男 18’ /tmp/student.txt
1 zhangsan 男 20
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
1.1 wanglun 男 18
多命令
你也可以把多 个 sed 命令合并到一个文件中,这个文件被称为 sed 脚本,然后使用-f 选项调用它。也可以使用 –e 选项,执行多个 sed 命令
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n -e ‘/^[1]/p’ -e ‘/^[3]/p’ -e ‘/^[5]/p’ /tmp/student.txt
1 zhangsan 男 20
3 wangwu 男 22
5 zhaogang 男 22
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n -f /tmp/testscript.sed /tmp/student.txt
1 zhangsan 男 20
3 wangwu 男 22
5 zhaogang 男 22
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# cat /tmp/testscript.sed
/^[1]/p
/^[3]/p
/^[5]/p
打印模式空间(命p)
使用命令p,可以打印当前模式空间的内容。
sed在执行命令后或默认打印模式空间的内容,那么为什么还需要命令p呢?
有如下原因,命令p可以控制只输出你制定的内容,通常使用p时,还需要使用-n选项来屏蔽sed的默认输出,否则当执行命令p时,每行记录会输出两次。
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed ‘p’ /tmp/student.txt
1 zhangsan 男 20
1 zhangsan 男 20
2 lisi 男 21
2 lisi 男 21
3 wangwu 男 22
3 wangwu 男 22
4 zhaoliu 女 23
4 zhaoliu 女 23
5 zhaogang 男 22
5 zhaogang 男 22
6 lihong 女 22
6 lihong 女 22
[zhangsan@iZ2ze9v82ax0ox0j12ahfhZ ~]# sed -n ‘p’ /tmp/student.txt
1 zhangsan 男 20
2 lisi 男 21
3 wangwu 男 22
4 zhaoliu 女 23
5 zhaogang 男 22
6 lihong 女 22
参考链接:
https://linux.die.net/man/1/sed
https://www.gnu.org/software/sed/manual/sed.html#sed-addresses