一、sed是什么
sed 命令是利用脚本来处理文本文件。它可以依照脚本的指令来处理、编辑文本文件。主要用来自动编 辑一个或多个文件、简化对文件的反复操作、编写转换程序等。
二、sed的原理
读入新的一行内容到缓存空间;
从指定的操作指令中取出第一条指令,判断是否匹配pattern;
如果不匹配,则忽略后续的编辑命令,回到第2步继续取出下一条指令;
如果匹配,则针对缓存的行执行后续的编辑命令;
完成后,回到第2步继续取出下一条指令;
当所有指令都应用之后,输出缓存行的内容;回到第1步继续读入下一行内容;
当所有行都处理完之后,结束;
三、sed的常用操作选项
sed [选项] '操作' 参数
sed [选项] -f scriptfile 参数
常见的 sed 命令选项主要包含以下几种。
-e 或--expression=:表示用指定命令或者脚本来处理输入的文本文件。同时编辑
-f 或--file=:表示用指定的脚本文件来处理输入的文本文件。
-h 或--help:显示帮助。
-n、--quiet 或 silent:表示仅显示处理后的结果。关闭打印
-i.bak:直接编辑文本文件。
-r, -E 使用扩展正则表达式
-s 将多个文件视为独立文件,而不是单个连续的长文件流
“操作”用于指定对文件操作的动作行为,也就是 sed 的命令。通常情况下是采用的“[n1[,n2]]”操作参数的格式。n1、n2 是可选的,代表选择进行操作的行数,如操作需要在 5~ 20 行之间进行,则表示为“5, 20 动作行为”。常见的操作包括以下几种。
a:增加,在当前行下面增加一行指定内容。
c:替换,将选定行替换为指定内容。
d:删除,删除选定的行。
i:插入,在选定行上面插入一行指定内容。
p:打印,如果同时指定行,表示打印指定行;如果不指定行,则表示打印所有内容;如果有非打印字符,则以 ASCII 码输出。其通常与“-n”选项一起使用。
s:替换,替换指定字符。
y:字符转换。
简单版
选项 含义
-e 进行多次编辑
-n 取消默认输出
-f 指定sed文件名
-i 直接在源文件中修改
-r 使用扩展正则表达式
sed常用命令动作
命令动作 含义
p 打印输出
d 删除指定行
i 在指定行之前插入内容
a 在指定行后面插入内容
c 替换指定行所有内容
s 搜索替换
四、如何使用sed
sed [options] script filename
sed [option]... | 'script;script;...' | [input file...] |
选项 | 自身脚本语法 | 支持标准输入管道 |
options指的是sed的命令行参数,比较有限,这个后面会说明。
script是指需要对输入执行的一个或者多个操作指令, 一般需要用单引号括起来,这样可以避免shell对 特殊字符的处理。 sed会依次读取输入文件的每一行到缓存中并应用script中指定的操作指令,因此而带 来的变化并不会影响最初的文件(除非option加了-i参数)。
每条操作指令由pattern和procedure两部分组成,顾名思义, pattern是匹配的规则, 一般为用’/'分隔的 正则表达式(也有可能是行号,具体参见Sed命令地址匹配问题总结),而procedure则是一连串编辑命 令(action)。
总结: sed命令+选项 ‘定位+动作+内容’ 文件名
五、具体操作
1、打印输出
[root@localhost ~]# cat -n /etc/passwd > /tmp/passwd #-n 在行前面标序号
[root@localhost ~]# sed '' /tmp/passwd #查看文件内容
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@localhost ~]# sed 'p' /tmp/passwd
#带有自动打印功能,p又再打印一遍
[root@localhost ~]# sed -n 'p' /tmp/passwd
-n 选项关闭自动打印功能 , p 打印
1.1 显示范围 行号(=)
[root@localhost ~]# sed -n '1p' passwd1 # 直接显示第1行
[root@localhost ~]# sed -n '1,3p' /tmp/passwd #显示1~3行范围
[root@localhost ~]# sed -n '1p;3p;5p' /tmp/passwd # 显示第1,3,5 行
[root@localhost ~]# sed -n '3,+3p' /tmp/passwd #从第3行 往后加3行
1.2 奇数偶数表示
[root@localhost ~]# sed -n '1~2p' /tmp/passwd #奇数
#1从第一行开始,2选择每隔两行的行,p打印
[root@localhost ~]# sed -n '2~2p' /tmp/passwd #偶数
#从第二行开始,2选择每隔两行的行,p打印出来
[root@localhost ~]# sed -n '2~3p' /tmp/passwd
#从第二行开始,3选择每隔三行的行,p并打印出来
[root@localhost ~]# sed -n '$p' /tmp/passwd #最后一行
[root@localhost ~]# sed -n '/root/p' /tmp/passwd
#将包含root的行打印出来 /root(需要匹配的内容)/p(打印) 文件名 / / 是包含的意思
#与 grep root /etc/passwd 功能相同
[root@localhost ~]# sed -n '/bash$/p' /tmp/passwd #将bash结尾 打印出来 文件名
[root@localhost ~]# sed -n '/s...x/p' /tmp/passwd
#/s...x/ 匹配包含一个字母 s,后面跟着任意三个字符,最后是一个字母 x 的行 p打印
[root@localhost ~]# sed -n '/[0-9]/p' /etc/passwd
#/[0-9]/ 匹配包含至少一个数字的行
[root@localhost ~]# sed -n '/^root/p' /etc/passwd
#包含以root开头的行
[root@localhost ~]# sed -n '1!p' /tmp/passwd
#1!p 的意思是除了第一行之外的所有行都会被打印出来
[root@localhost ~]# sed -n '/nologin/!p' /tmp/passwd
#除了nologin都能打印出来
[root@localhost ~]# sed -n '/root/=' passwd
#找到所有包含字符串 "root" 的行;对于每一行,输出它在文件中的行号。
[root@localhost ~]# sed -n '/root/=;/root/p' /tmp/passwd
//多个模式匹配用分号或者-e
输出包含root行的行号 ; 打印包含root的行
[root@localhost ~]# sed -n '$=' zz.txt
# 输出最后一行行号 ,相当于统计文件有几行
[root@localhost ~]# sed -n '$=;1p' zzz.txt
#可以验证是逐行匹配
sed默认不支持扩展正则,如果要支持,需要加-r选项
[root@localhost ~]# sed -nr '/^root|^shutdown/p' /etc/passwd
打印 以root开头 或 以shutdown开头
可结合管道
[root@localhost ~]# df -h | sed -n '2p'
注意:通常-n和‘p’一起使用
2、增加(i、a)
注意: a或者i后面的所有内容都会被理解为需要添加的内容
[root@localhost ~]# sed '2ihello world' /tmp/passwd
#2:在第2行 , i: 在指定的行之前插入文本hello world
[root@localhost ~]# sed '3ihello\nworld' /tmp/passwd
在第三行前打印hello world \n 换行
[root@localhost ~]# sed '1ahello world' passwd1
# 在当第一行下面增加一行
[root@localhost ~]# sed '$ahello world' /tmp/passwd
在最后一行下面添加hello world
[root@localhost ~]# sed '2,4a hello' /tmp/passwd
#在2~4行下面添加hello
[root@localhost ~]# sed '/shengjie/a hello world' /tmp/passwd
在包含root的行下面添加hello
注意: a或者i后面的所有内容都会被理解为需要添加的内容
[root@localhost ~]# sed '/shengjie/a hello world;3p;3i shell' /tmp/passwd
在包含root的行下面添加 hello world;3p;3i shell
3、删除(d)
[root@localhost ~]# sed 'd' passwd
删除passwd里的所有内容
[root@localhost ~]# sed '1d' /tmp/passwd
删除第1行
[root@localhost ~]# sed '1,4d' /tmp/passwd
删除1到4行
[root@localhost ~]# sed '/nologin/d' /tmp/passwd
删除包含nologin的行
[root@localhost ~]# echo '' >> /tmp/passwd 在最后一行行尾添加空格
[root@localhost ~]# echo '###' >> /tmp/passwd 在最后一行行尾添加###
[root@localhost ~]# sed '/^#/d' /tmp/passwd 删除以#开头的行
[root@localhost ~]# sed '/^#/d;/^$/d' /tmp/passwd 删除以#开头的行 ;空行
4、整行替换(c)
s/pattern/string/修饰符 查找替换 ,支持使用其它分隔符,可以是其它形式:s@@@,s### 替换修饰符:
g 行内全局替换
p 显示替换成功的行
w /PATH/FILE 将替换成功的行保存至文件中
I,i 忽略大小写
[root@localhost ~]# sed '/^root/ckgc' /etc/passwd
以root开头的整行替换成kgc
[root@localhost ~]# sed '/root/ckgc' /etc/passwd
包含root的整行替换成kgc
[root@localhost ~]# sed 'ckgc' /etc/passwd
所有行替换成kgc
这里没有真的改变文件内容,只是输出到屏幕,如果想要真的替换,需要用-i选项,建议用-i之前对原文件进行备份 -i是真的修改
[root@localhost ~]# sed -n -i.bak 'p' /etc/hosts
给passwd 备份 passwd.bak passwd.bak内容和passwd一样
5、搜索替换(s)
c指令使得整行内容全部替换, d指令删除整行,
s 只替换某个关键词
格式: sed 选项 ‘s/搜索的内容/替换的内容/动作’
[root@localhost ~]# sed 's/root/ROOT/' /tmp/passwd
搜索root所在行把第一个出现的root换成ROOT
[root@localhost ~]# sed -n 's/root/ROOT/gp' /tmp/passwd
搜索root所在行,把所有出现的root都替换成ROOT g:全局
不加-n p 打印全部
加-n p 只打印有root行
[root@localhost ~]# sed -n 's/o/O/2p' /tmp/passwd
搜索字母o ,仅替换每行第二个o为大写O
[root@localhost ~]# sed -n 's/root//gp' /tmp/passwd
将全局所有root替换为空
[root@localhost ~]# sed -n '1,5s/^/#/' /tmp/passwd
把1-5行的开头都插入#号
[root@localhost ~]# sed -n 's/\/sbin\/nologin/kgc/gp' /tmp/passwd
把所有/sbin/nologin换成kgc ,/需要转义 , \/sbin\/nologin
但是使用默认的转义符之后阅读体验非常差,而且还容易写错,我们可以指定更有辨识度的转义符,例 如@,#,数字都可以,将原来的/替换成我们想要的
[root@localhost ~]# sed -n 's@/sbin/nologin@kgc@gp' /tmp/passwd
把所有 / 替换成 @ 或 # /sbin/nologin的 / 不换
我们只想搜索以root开头的行并且前面加#号,那就需要保留root,那就要把搜索的字符写成&就会被保留不会一起替换,观察不加&的区别
[root@localhost ~]# sed -n 's/^root/#&/gp' /etc/passwd
以 root 开头的行在行首加 # 并且保留 root
忽略大小写的替换
[root@localhost ~]# sed -n 's/network/ooooo/igp' /tmp/passwd
把所有无论大小写的 network 替换成 ooooo
生产案列
sed -i.bak 's/SELINUX=enable/SELINUX=disabled/' /etc/selinux/config
把 /etc/selinux/config 生成备份文件 config.bak
并把 SELINUX=enable 替换成SELINUX=disabled
6、插入文件(r)
我们还可以将其他文件插入到当前文件当中处理
[root@localhost ~]# sed '3r /etc/hosts' passwd1
将/etc/hosts文件插入到 passwd1 第三行后面
[root@localhost ~]# sed '/2/r /etc/hosts' passwd1
将 /etc/hosts 文件插入到passwd1 含2的行下面
[root@localhost ~]# sed '$r /etc/hosts' passwd1
把 /etc/hosts 插入到passwd1最后一行下面
7、另存为到文件(w)
使用w指令将当前编辑的文件内容另存到其他文件中,如果目标文件已存在,则会将目标文件的内容覆盖
[root@localhost ~]# sed 'w passwd' /etc/hosts #把 /etc/hosts 复制到 passwd
如果passwd 有内容, 则覆盖
如果没有文件,则创建文件并复制
文件passwd1 已存在 ,把 /etc/hosts 复制到 passwd1 下,并覆盖
若没有文件 hosts ,创建文件hosts,并把 /etc/hosts 复制到 hosts 下
如果 hosts 文件不存在,sed 命令会创建它;如果存在,sed 将会覆盖该文件的内容。
如果你想将 /etc/hosts 文件的内容追加到 hosts 文件末尾,可以使用 >> 符号来实现,如
sed 'w /tmp/hosts' /etc/hosts >> /tmp/hosts
把 /etc/hosts 复制到 hosts文件下,并追加到 hosts ,不覆盖
[root@localhost ~]# sed '1w hosts' /etc/hosts
# 从/etc/hosts 中的第一行另存为到 hosts
8、同时编辑(-e)
sed支持一个或多个-e参数
[root@localhost ~]# sed -n -e '1p' -e '3p' /tmp/passwd
[root@localhost ~]# sed -ne '1p' -ne '5p' /etc/passwd
表示用指定命令或者脚本来处理打印第一行和第三行
9、分组操作 {}
当我们需要对一行数据进行多次操作的时候我们可以使用{}进行分组
[root@localhost ~]# sed '/root/{s/root/ROOT/;s/x/X/g}' passwd1
把包含 root 的行 ,root替换成 ROOT ,本行所有 x 替换成 X (g 全局)
[root@localhost ~]# sed -ne '/root/{s/root/ROOT/;s/x/X/g}' -ne '1,10p' passwd1
-ne 同时编辑 包含 root 的行 ,root 替换成 ROOT,并将行中所有 x 替换成 X
-ne 同时编辑 打印1到10行
###分组 s/ /代表查找替换 () 代表分组 \1 代表留下的第一组 r:读取指定文件;
[root@localhost ~]# echo 123abcxyz |sed -r 's/(123)(abc)(xyz)/\1/'
#####从 ifconfig 命令的输出中提取 ens33 网卡的 IP 地址。通过 sed 的正则表达式匹配,它会找到 包含 inet 字段的行,并从中提取出 IP 地址部分,然后输出这个 IP 地址
输出 ens33 的 IP
[root@localhost ~]# ifconfig ens33|sed -rn '2s/.*inet ([0-9.]+) .*/\1/p'
2s 第二行 .*inet 任意内容inet ([0-9.]+) 0-9. +匹配前面内容 \1第一组
10、读取完退出(q)
正常情况下sed会在读取完所有数据行之后退出,但是我们可以随时使用q指令来提前退出sed
[root@localhost ~]# sed '3q' passwd1 #表示仅显示处理后的结果
显示前3行
注意: q不要和-i一起使用,以免覆盖源文件
11、sed脚本(-f)
有时我们需要对文件进行的增、删、改比较多,但是一条一条修改比较麻烦,所以对于指令比较多的情况,我们可以先将所有的sed指令写入一个文本文件中,然后通过sed的-f选项读取该指令文件即可实现多指令操作
使用 sed 命令迁移符合条件的文本时,常用到以下参数 .
H :复制到剪贴板;
g、G :将剪贴板中的数据覆盖/追加至指定行;
w :保存为文件;
r :读取指定文件;
a :追加指定内容
I,i 忽略大小写
[root@localhost ~]#sed '/the/{H;d};$G' test.txt #把包含 the 行剪切到文件末尾
##将包含the 的行迁移至文件末尾 ,{;}用于多个操作
{H;d} 先复制再删除(剪切)$G 到文件末尾
[root@localhost ~]#sed '1,5{H;d};15G' test.txt #1到5行剪切到15行后面
##将第 1~5 行内容转移至第 17 行后
[root@localhost ~]# vim shell.list
#! /bin/sed -f
1,5H
1,5d 1到5行 剪切到目标文件16行下面
16G
[root@localhost ~]# sed -f shell.list ky35
shell.list 脚本运用在 ky35 文件中
|| 表示上一条命令执行失败后,才执行下一条命令
使用脚本修改
[root@localhost ~]# vim test.sh
#!/bin/sed -f
s/root/ROOT/g 把全局root替换成ROOT
s/x/X/g 把全局x替换成X
s/\/sbin\/nologin/kgc/g 把全局/sbin/nologin替换成kgc
[root@localhost ~]# sed -f test.sh passwd1
#!/bin/sed -f
1c hello world
2{
p
s/b/B/
}
/root/{
s/x/H/
s/root/ROOT/
}
注意:
. sed脚本文件第一行要声明#!/bin/sed -f
不要加单引号
每行的最后不能有空格等多余字符 . #号开头为注释
一行有多个命令时用分号隔开
注意: sed的返回值一般情况为0,不管是不是修改成功了,除非是语法错误 所以sed的返回值$?一般不作为sed成功的判断条件
12、sed的高级应用
1、-r 匹配正则
[root@localhost ~]# sed -r s/^[\t]*/#/ /etc/hosts.bak
将每一行开头的空白字符替换为
#
2、结合变量使用
[root@localhost ~]# word=1111 #定义一个变量
[root@localhost ~]# sed '1a$word' /etc/hosts
在 /etc/hosts 第一行下面添加 $word ,(单引号把任何字符当普通字符看待)
[root@localhost ~]# sed "1a$word" /etc/hosts #改成双引号就可以了
在/etc/hosts 第一行下面添加 word 里的内容
[root@localhost ~]# sed '1a'"$word" /etc/hosts #或者也可以各用各的符号
[root@localhost ~]# sed 1a$word /etc/hosts #不用也可以
[root@localhost ~]# sed '$a'"$word" /etc/hosts
#但是如果遇到想在最后一行后面添加的话就不能用双引号或者不用符号
在 /etc/hosts 最后一行下面添加 word 里的内容
[root@localhost ~]# sed "\$a$word" /etc/hosts #如果要用双引号必须用转义符
13、生产案例
####sed 直接操作文件示例 生产案列
案列 1
需求:需要将ens33网络接口的IP地址修改为 192.168.190.31
cat /etc/sysconfig/network-scripts/ifcfg-ens33
sed -i 's/^IPADDR=.*/IPADDR=192.168.10.100/' /etc/sysconfig/network- scripts/ifcfg-ens33
#在原文件中修改,把以IPADDR开头=任意内容的行 替换成 IPADDR=192.168.190.30
sed -n '/^IPADDR/p' /etc/sysconfig/network-scripts/ifcfg-ens33
#打印包含以IPADDR开头的行
IPADDR=192.168.190.30
案列 2
需求:需要将 Apache 的监听地址修改为 192.168.10.100,端口修改为 8080 # 修改监听地址
sudo sed -i 's/^Listen .*/Listen 192.168.10.100:8080/' /etc/httpd/conf/httpd.conf
# 修改 ServerName
sed -i 's/^ServerName .*/ServerName 192.168.10.100:8080/' /etc/httpd/conf/httpd.conf
编写一个脚本,用来调整 vsftpd 服务配置,要求禁止匿名用户,但允许本地用户(也允许写入)。
案列3 脚本
[root@lo calhost ~]# vim local_only_ftp.sh #!/bin/bash
# 指定样本文件路径、配置文件路径
SAMPLE="/usr/share/doc/vsftpd-3.0.2/EXAMPLE/INTERNET_SITE/vsftpd.conf " CONFIG="/etc/vsftpd/vsftpd.conf"
# 备份原来的在 , 若不存在则使用 cp 命令进行配置文件 ,检测文件名为/etc/vsftpd/vsftpd.conf.bak 备份文件是否存文件备份
[ ! -e "$CONFIG.bak" ] && cp $CONFIG $CONFIG.bak # 基于样本配置进行调整 ,覆盖现有文件 sed -e '/^anonymous_enable/s/YES/NO/g' $SAMPLE > $CONFIG
sed -i -e '/^local_enable/s/NO/YES/g' -e '/^write_enable/s/NO/YES/g' $CONFIG grep "listen" $CONFIG || sed -i '$alisten=YES' $CONFIG
# 启动vsftpd 服务 ,并设为开机后自动运行 systemctl restart vsftpd
systemctl enable vsftpd
[root@localhost ~]# chmod +x local_only_ftp.sh