第九章 shell编程之sed
概念
-
sed命令是一个==非交互式==的文本编辑器,是将一系列的编辑命令应用于一批文本的理想工具,可以对来自文本文件以及标准输入的文本进行编辑。其中,标准输入可以是来自键盘、文件重定向、字符串、变量或者是管道的文本
-
sed命令拥有非交互式和高效的特点,可以为用户节约大量的时间
-
Vim 采用的是交互式文本编辑模式,可以用键盘命令来交互性地插入、删除或替换数据中的文本。但 sed命令不同,它采用的是流编辑模式,最明显的特点是,在 sed 处理数据之前,需要预先提供一组规则,sed 会按照此规则来编辑数据
工作原理:
-
模式空间pattern space:sed在内存里开辟模式空间,处理文件的每个输入行,最多8192字节
-
保留空间holding space:sed在内存里开辟保留空间,保存已经处理过的输入行,最多8192字节
-
原理:sed 的工作流程主要包括读取、执行和显示三个过程:
-
读取:sed 从输入流(文件、管道、标准输入)中读取一行内容并存储到临时的缓冲区中(又称模式空间,pattern space)。
-
执行:默认情况下,所有的sed 命令都在模式空间中顺序地执行,除非指定了行的地址,否则sed 命令 将会在所有的行上依次执行。
-
显示:发送修改后的内容到输出流。在发送数据后,模式空间将会被清空
-
重复上述过程,直到将文件中所有数据处理完毕
-
-
图:
-
注意:默认情况下所有的sed命令都是在模式空间内执行的,因此输入的文件并不会发生任何变化,除非是用重定向存储输出
基本语法
格式
sed -参数 '[定址符]' '操作' 文件名
参数
-
-n,--quiet,--silent:安静模式,不输出模式空间中的内容,在一般sed的用法中,所有来自STDIN的数据一般都会被列出到屏幕上,但如果加上-n参数后,则只有经过sed特殊处理的那一行才会被列出来
-
-i:==直接编辑原文件,而不是由屏幕输出,默认不对原文件进行操作==
-
-e:直接在命令行模式上进行sed的动作编辑,多个子命令之间也可以用分号隔开,如:sed 'command1;command2... filename 或者 sed -e 'command1' -e command2' ……filename
-
-r:使用扩展正则表达式
-
-f:直接将sed的动作写在一个文件内,-f filename则可以执行filename内的sed动作
-
--help:显示帮助
-
--version:显示版本
-
-{} :可组合多个命令,以分好隔开
定址符
-
用于使用数字指定处理的行区间,或者使用正则进行过滤
-
表示方法
地址定界 | 作用 |
---|---|
1,5 | 对文件的1-5行内容进行处理 |
2,$ | 对文件的2到最后一行内容进行处理 |
1,+3 | 对文件第1行以及以后的3行内容进行处理 |
1~2 | 对文件的1,3,5,7,……的行内容进行处理 |
/正则表达式/ | 对任何能够被正则表达式匹配到的行进行处理 |
操作
s:替换,替换指定字符,一般搭配正则表达式
d:删除,删除选定的行。
p:打印,如果同时指定行,表示打印指定行;如果不指定行,则表示打印所有内容;如果有非打印字符,则以 ASCII 码输出。其通常与“-n”选项一起使用。
=:打印行号。
a:增加,a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)
i:插入,i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行)
c:行替换,指定行中的所有内容,替换成该选项后面的字符串。
y:字符转换,转换前后的字符长度必须相同。
l(小写):打印数据流中的文本和不可打印的ASCII字符(比如结束符$、制表符\t)
! :对指定行以外的所有行应用命令
& :代表匹配到的内容
输出文本
范例文件:
[root@server ~]# vim sed1.txt
one
two
three
four
five
six
seven
eight
nine
ten
示例
[root@server ~]# sed -n 'p' sed1.txt # 全文打印
[root@server ~]# sed -n '=;p' sed1.txt # 同时打印行号
[root@server ~]# sed -n -e '=' -e 'p' sed1.txt # 同上
[root@server ~]# sed -n 'l' sed1.txt # 同时输出内容及控制字符
[root@server ~]# sed -n ' # 交互模式
> =
> p
> ' sed1.txt
[root@server ~]# sed -n '1p' sed1.txt # 打印第一行
[root@server ~]# sed -n '4,6p' sed1.txt # 打印4-6行
[root@server ~]# sed -n '3,9p' sed1.txt # 打印中3到9行内容
[root@server ~]# sed -n '7,$p' sed1.txt # 第7行到最后一行
[root@server ~]# sed -n '$p' sed1.txt # 打印最后一行
[root@server ~]# sed -n '2~2p' sed1.txt # 打印偶数行
[root@server ~]# sed -n '1~2p' sed1.txt # 打印奇数行
[root@server ~]# sed -n '4p;6p;8p' sed1.txt # 打印第4、6、8行
[root@server ~]# sed -n '1,+5p' sed1.txt # 打印第一行及其下5行
[root@server ~]# sed -n '1!p' sed1.txt # 打印除了第一行的剩余行
[root@server ~]# sed -n '/^f/p' sed1.txt # 正则检索以f开头
[root@server ~]# sed -n '/t/p' sed1.txt # 正则检索包含t的行
[root@server ~]# sed -n '/^t/p;/^f/p' sed1.txt # 正则检索以t开头或者f开头的行
[root@server ~]# sed -n '2,/five/p' sed1.txt # 检索第2行到five所在行的内容
[root@server ~]# sed -n '/n$/p' sed1.txt # 正则检索以n结尾的行
[root@server ~]# sed -n 'n;p' sed1.txt # 检索偶数行
[root@server ~]# sed -n 'p;n' sed1.txt # 检索奇数行
[root@server ~]# sed -n '$=' sed1.txt # 打印文本行数
文本替换
范例文件
[root@server ~]# cp /etc/passwd ~/ps
[root@server ~]# cat ps
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
……
……
格式:
sed '行范围s/旧字符串/新字符串/替换标记' 文件名
-
行范围:使用数字表示,即对第几行到第几行进行替换处理,若省略则表示进行全文处理
-
s: 替换动作
-
/// :表示替换格式
-
替换标记
-
数字:使用1-512之间的数字,表示指定需要替换的字符串出现第几次才进行替换
-
g:对数据中所有匹配到的内容进行替换,若省略g则只会对第一次匹配的内容进行替换
-
p:替换成功立刻打印,一般与-n一起使用
-
w 文件名 : 将缓冲区的内容写入到指定文件中
-
& : 使用正则表达式匹配的内容进行替换
-
\ :转义符,若有特殊符号或路径则需要转义
-
示例
[root@server ~]# sed 's/root/boot/g' ps # 替换root为boot
[root@server ~]# sed -n 's/root/admin/p' ps # 将每一行的第一个root替换为admin
[root@server ~]# sed -n 's/root/admin/2p' ps # 将本行第2次出现的root进行替换
[root@server ~]# sed -n 's/root/admin/gp' ps # 将文本中所有root全部替换
[root@server ~]# sed -n '4s/adm/root/gp' ps # 将第4行内容替换
# 将ps文件第4行的/sbin/nologin替换为/bin/bash,注意:路径的/必须要转义
[root@server ~]# sed -n '5s/\/sbin\/nologin/\/bin\/bash/p' ps
[root@server ~]# sed 's#/sbin/nologin#/bin/bash#g' ps # 同上,/可写为#
[root@server ~]# sed 's#root#boot#g' ps # 同上,但内容为#不能更改
[root@server ~]# sed -n '$s/:/@/gp' ps # 最后一行冒号替换为@
# 全文注释:即行首添加# ,#&表示在定位位置的左侧添加#
[root@server ~]# sed 's/^/#&/' ps
[root@server ~]# sed 's/^/#/' ps 可省略&
# 检索SSH行,在其单词左侧添加#,不能省略&,否则为替换
[root@server ~]# sed 's/SSH/#&/' ps
[root@server ~]# sed 's/SSH/&#/' ps # 检索SSH行,&#表示在其单词右侧添加#
[root@server ~]# sed 's/^/@&/g;s/$/&@/g' ps # 所有行首及行尾添加@,可省略&
[root@server ~]# sed '1s/^/\n/' ps # 第一行之前插入空白行
[root@server ~]# sed '$s/$/\n/' ps # 最后一行之后插入空白行
# 所有小写改大写,\u表示大写,&启用正则匹配
[root@server ~]# sed 's/[a-z]/\u&/g' ps
# 所有大写改小写,\l(注意是小写的L)表示小写,&启用正则匹配
[root@server ~]# sed 's/[A-Z]/\L&/g' ps
# 单词第一个字母大写,\b表示匹配词首边界
[root@server ~]# sed 's/\b[a-z]/\u&/g' ps
# 开启selinux
[root@server ~]# sed -i '7s/disabled/enforcing/' /etc/selinux/config
# 将7行的disabled替换为enforcing
# 使用sed更换httpd的网页默认目录
[root@server ~]# yum install nginx -y
[root@server ~]# sed -i '42s/\/usr\/share\/nginx\/html/\/www\/zy/' /etc/nginx/nginx.conf
# 使用替换实现删除
[root@server ~]# sed -n 's/root//gp' ps # 将全文中root替换为空,即删除
[root@server ~]# sed -n 's/^.//p' ps # 删除每行第一个字符,^.表示行首任意一个字符
[root@server ~]# sed -n 's/^#//p' /root/anaconda-ks.cfg # 将注释符#删除
[root@server ~]# sed -n 's/.$//p' ps # 删除每行最后一个字符
[root@server ~]# sed '5,10s/[0-9]//g' ps # 删除5-10行的所有数字
# 删除所有的特殊字符(除了数字及大小写字母),将结果写入到t1.txt文件中
[root@server ~]# sed 's/[^(a-z)(A-Z)(0-9)]//gw t1.txt' ps
删除文本
注意
-
d操作用于删除文本的特定行,会删除指定的所有内容,则使用该命令必须特别小心,若忘记指定处理行的话会删除所有内容,也不会有任何输出
[root@server ~]# sed -i 'd' ps # 全部清空
[root@server ~]# cat ps
示例
[root@server ~]# cp /root/anaconda-ks.cfg / # 示例文件准备
[root@server ~]# sed '1d' /anaconda-ks.cfg # 删除第1行
[root@server ~]# sed '2,5d' /anaconda-ks.cfg # 删除2、5行
[root@server ~]# sed '8,$d' /anaconda-ks.cfg # 删除第8到结尾所有行
[root@server ~]# sed '$d' /anaconda-ks.cfg # 删除最后一行
[root@server ~]# line_number=1
[root@server ~]# sed "${line_number}d" /anaconda-ks.cfg # 删除某行,使用变量作为行号
[root@server ~]# sed '/^$/d' /anaconda-ks.cfg # 删除空白行
[root@server ~]# sed '/System/d' /anaconda-ks.cfg # 将包含Options的整行删除
[root@server ~]# sed -e '/System/d' -e '/Root/d' /anaconda-ks.cfg # 多个关键字
[root@server ~]# sed '/System/d' sos.conf | sed '/Root/d' /anaconda-ks.cfg # 同上
[root@server ~]# sed '/3/,/5/d' /anaconda-ks.cfg # 删除3所在行到5所在行之间的内容(包含)
[root@server ~]# sed '/^#/d' /anaconda-ks.cfg
# 注意:默认情况下,删除的内容只是在输出的结果中消失了,没有增减-i参数时源文件不变
插入文本
注意
-
使用a(append)动作在指定行的下一行追加一行,使用i(insert)动作在指定行前插入一行,两者格式相同
格式
sed '行范围a(或i)\新文本' 文件名
示例1
[root@server ~]# cat /etc/hosts
[root@server ~]# cp /etc/hosts ~/
# 尾部添加一行
[root@server ~]# sed -i '$a\192.168.48.132 www.openlab.com' ~/hosts
# 某行之前添加
[root@server ~]# sed -i '3i\192.168.48.131 www.openlab.com' ~/hosts
# 文本匹配后插入
[root@server ~]# sed -i '/131/i\192.168.48.130 www.openlab.com' ~/hosts
注意
-
sed基于数据流处理的命令,若无内容则无法处理,所以空文件不能插入
[root@server ~]# touch t2.txt
[root@server ~]# cat t2.txt
[root@server ~]# sed -i '$a\xiao ming,18,98.5' t2.txt
[root@server ~]# cat t2.txt
练习
-
习题1
示例:把/etc/passwd 复制到/root/test.txt,用sed打印所有行;
1、打印test.txt的3到10行;
2、打印test.txt 中包含’root’的行;
3、删除test.txt 的15行以及以后所有行;
4、删除test.txt中包含’bash’的行;
5、替换test.txt 中’root’为’toor’;
6、替换test.txt中’/sbin/nologin’为’/bin/login’
7、删除test.txt中5到10行中所有的数字;
8、删除test.txt 中所有特殊字符(除了数字以及大小写字母);
9、在test.txt 20行到末行最前面加’aaa:’
10、在test.txt所有行首增加#注释
-
习题2
# 打印4-8行
# 打印第3行及其下2行
# 打印中包含null的行
# 打印第5行到sshd所在行的内容
# 将全文的替换adm替换为admin
# 将全文的/bin/bash替换为/usr/bin/sh
# 将1-5行的单词第一个字母大写
# 在第5行之前插入一个空白行
# 安装bind,将/etc/named.conf的2项参数替换为any
# 删除所有的冒号
# 在最后一行之后添加一个空白行
# 删除root所在行
# 在第一行之前插入一条信息记录,内容自定
-
面试题1:
[root@server ~]# vim nowcoder.txt
zhangsan 30 18567198188
wangwu 20 13390789090
lisi 26 15129883716
[root@server ~]# cat nowcoder.txt
zhangsan 30 18567198188
wangwu 20 13390789090
lisi 26 15129883716
[root@server ~]# sed -r 's/([1-9]{3})([0-9]{4})([0-9]{4})$/\1-\2-\3/g' nowcoder.txt
-
面试题2:
# 文件内容如下:
123abc456
456def123
567abc789
789def567
# 要求输出:
456ABC123
123DEF456
789ABC567
567DEF789
[root@server ~]# sed -r 's/([0-9]{3})(.*)([0-9]{3})/\3\2\1/' b.txt | tr -s '[a-z]' '[A-Z]'
456ABC123
123DEF456
789ABC567
567DEF789