sed详解

一、什么是sed

sed全称(stream editor)流式编辑器,Sed主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等,工作流程如下

sed 是一种在线的、非交互式的编辑器,它一次处理一行内容。处理时,把当前处理的行存储在
临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完
成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有
改变,除非你使用重定向存储输出,或者使用sed -i选项
-i选项就是将本该输出到屏幕上的内容输出/流入文件中

sed命令格式如下

sed [options] 'command' file(s)
sed [options] -f scriptfile file(s)

# 注:
sed和grep不一样,不管是否找到指定的模式,它的退出状态都是0
只有当命令存在语法错误时,sed的退出状态才不是0

二、 sed和vim的区别

  1. sed可以把处理文件的规则事先写好,然后用一套规则处理,vim只能一个一个编辑,成批次处理用sed处理比较好
  2. sed处理文件一次内存中只有一行,不会对内存造成过大的压力。

三、作用

作用: 可以直接编辑修改文件内容
可以对文件进行增删改查
语法:
sed 参数 "[定位 指令]" 文件路径

参数:

-e 允许多项编辑
-r 扩展正则
-n 取消默认输出(模式空间的内容输出)
-i 写入文件  改变了输出流向,对源文件修改 (把流向屏幕的内容,流入文件中)
-i.bak 修改文件时会创建备份文件,防止手误
-a 指定行后添加信息
-i 指定行前添加信息
-f 指定sed脚本文件名

指令:

 -p 打印到屏幕
 -d 删除
 -I忽略大小写
 !表示取反
 s/// 替换
 -e 识别多条指令 
 -c将整行内容清除后替换为c后面指定的内容
 指令可以用;号连接多条,如1d;3d;5d代表删除1,3,5行

四、定位( pattern过滤)

1.行定位:

'1' 表示定位到第一行
"1,3" 代表从第一行到第三行
不写定位代表点位所有行
1.打印/etc/passwd文件的第10[root@manager ~]# sed -n '10p' /etc/passwd

2.打印第5行到第10[root@manager ~]# sed -n '5,10p' /etc/passwd

3.从第5行开始,往后打印5,包括第5[root@manager ~]# sed -n '5,+5p' /etc/passwd

4.打印/etc/passwd中开头为root的行开始,到开头为ftp的行结束的内容
root@manager ~]# sed -n '/^root/,/ftp/p' /etc/passwd
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
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

5.打印/etc/passwd中第8行开始,到含有/sbin/nologin的内容的行结束内容
[root@manager ~]# sed -n '8,/sbin/nologin/p' /etc/passwd

2.正则表达式定位

与grep一样,sed在文件中查找模式时也可以使用正则表达式(RE)和各种元字符。正则表达式是
括在斜杠间(/xxx/)的模式,用于查找和替换,以下是sed支持的元字符。

# 使用基本元字符集	
^, $, ., *, [], [^], < >,(),{}

# 使用扩展元字符集	
?, +, { }, |, ( )

# 使用扩展元字符的方式:
转义,如+
-r参数,如sed -r

"/love/"  包含love的行
"/^love/" 以love开头的行
"/love$/" 以love结尾的行

数字+正则表达式定位

"1,8p" 表示打印18"1,/love/p" 代表从第1行到首次匹配到/love/的行

注: 当前的所有操作,只是在模式空间修改,如果真正修改文件, 只需要加 -i 参数,不加-n ,指令加-p

五、 查操作

根据内容筛选文件信息
根据行号筛查文件信息

[root@localhost ~]# sed -n "/root/p" /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

根据内容过滤多行连续的信息

在这里插入代码片

根据内容筛选文件信息

[root@localhost ~]# sed -rn '/^#|^$/!p' /etc/selinux/config 
SELINUX=enforcing
SELINUXTYPE=targeted 

根据行号筛选文件信息
例如,如下文件

[root@localhost ~]# cat -n /etc/selinux/config 
     1	
     2	# This file controls the state of SELinux on the system.
     3	# SELINUX= can take one of these three values:
     4	#     enforcing - SELinux security policy is enforced.
     5	#     permissive - SELinux prints warnings instead of enforcing.
     6	#     disabled - No SELinux policy is loaded.
     7	SELINUX=enforcing
     8	# SELINUXTYPE= can take one of three values:
     9	#     targeted - Targeted processes are protected,
    10	#     minimum - Modification of targeted policy. Only selected processes are protected. 
    11	#     mls - Multi Level Security protection.
    12	SELINUXTYPE=targeted 
    13	
    14	
[root@localhost ~]# 

取第7行

[root@localhost ~]# sed -n "7p" /etc/selinux/config 
SELINUX=enforcing

取一到三行:

[root@localhost ~]# sed -n "1,3p" /etc/selinux/config 

# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
[root@localhost ~]# 

取第七行或十二行

[root@localhost ~]# sed -n "7p;12p" /etc/selinux/config 
SELINUX=enforcing
SELINUXTYPE=targeted 

六、 删操作

删:-d

编辑命令 含义
1d 删除第1行的内容
1,5d 删除1行到5行的内容
2,+5d 删除第2行以及往下的5行的内容
/pattern1/d 删除每行中匹配到pattern1的行内容
/pattern1/,/pattern2/d 删除匹配到pattern1的行直到匹配到pattern2的所有行内容
/pattern1/,10d 删除匹配到pattern1的行到10行的所有行内容
10,/pattern1/d 删除第10行直到匹配到pattern1的所有内容
[root@localhost ~]# cat a.txt 
111
222
333

把第二行222删除
[root@localhost ~]# sed  "/222/d" a.txt
111
333

再次查看文件,发现并未删除,此处的删除只是发生在内存的模式空间中,-i可以覆盖回原文件

[root@localhost ~]# cat a.txt 
111
222
333
[root@localhost ~]# sed -i "/222/d" a.txt
[root@localhost ~]# cat a.txt 
111
333
[root@localhost ~]# 

修改文件之前,应该做到先备份 -i.bak 为后缀名,就会在当前目录产生一个备份文件,防止误删除

[root@localhost ~]# sed -i.bak "/222/d" a.txt 
[root@localhost ~]# ll
总用量 8
drwxr-xr-x. 3 root root 17 730 19:10 aaa
-rw-r--r--. 1 root root  8 731 07:34 a.txt
-rw-r--r--. 1 root root 12 731 07:30 a.txt.bak
[root@localhost ~]# cat a.txt
111
333
[root@localhost ~]# cat a.txt.bak 
111
222
333
[root@localhost ~]# 

切记:删除时 -i 和-n不能同时出现,防止未知的麻烦

练习:

1.删除 /etc/passwd 文件中第1行内容
[root@manager ~]# sed '1d' /etc/passwd

2.删除 /etc/passwd 文件中第1行到第5行的内容
[root@manager ~]# sed '1,5d' /etc/passwd

3.删除 /etc/passwd 文件中第2行以及往下的5行内容
[root@manager ~]# sed '2,+5d' /etc/passwd 

4.匹配 /sbin/nologin 结尾的行,然后进行删除。
[root@manager ~]# sed  '/sbin/nologin/d' /etc/passwd

5.删除/etc/passwd中以bin开头的行,到以ftp开头的行的所有内容
[root@manager ~]# sed '/^bin/',/^ftp/d /etc/passwd

6.删除/etc/passwd中第3行到以ftp开头的所有行内容
[root@manager ~]# sed '3,/^ftp/d' /etc/passwd

7删除nginx配置文件中注释和空行
	1.[root@manager ~]# sed '/#|^$/d' /etc/nginx/nginx.conf
	2.[root@manager ~]# sed -r '/#|^$/d' /etc/nginx/nginx.conf
	3.[root@manager ~]# sed -rn '/#|^$/!p' /etc/nginx/nginx.conf


七、 增加编辑命令

命令 含义
a 行后追加内容 append
i 行前追加内容 insert
r 读入外部文件,行后追加
w 将匹配行写入外部文件

若想在文件的指定地点增加一些信息

1. 在指定行的后面添加信息

行号a (append)
建议用单引号

[root@localhost ~]# sed "1ahello" a.txt
111
hello
222
333
[root@localhost ~]# sed "2ahello" a.txt
111
222
hello
333
[root@localhost ~]# sed "3ahello" a.txt
111
222
333
hello


[root@localhost ~]# sed "1,2ahello" a.txt # 第一行到第三行,每行后都增加hello
111
hello
222
hello
333

[root@manager ~]# sed -e '1ahello' -e '3ahello' a.txt # 第一行和第三行后添加hello
111
hello
222
333
hello

 
[root@localhost ~]# sed '$a hello' a.txt # 在最后一行末尾添加 用单引号
111
222
333
hello


将/etc/fstab文件的内容追加到a.txt文件的第2行后面
[root@manager ~]# sed '2r /etc/fstab' a.txt # 不能加a

将/etc/inittab文件内容追加到/etc/passwd文件匹配/bin/sync行的后面
sed '//bin/rsync/r /etc/inittab' /etc/passwd

将passwd文件匹配到/bin/bash的行追加到/tmp/a.txt文件中
[root@manager ~]# sed  '//bin/bash/w /tmp/a.txt' /etc/passwd

2. 在指定行前插入信息

行号i (insert)

[root@localhost ~]# sed "1ihello" a.txt
hello
111
222
333

3.在指定行的后面或前面添加多行信息
n 或者-e
方式1:

[root@localhost ~]# sed "1ahellonworld" a.txt # 在第一行后面添加两行hello world 
111
hello
world
222
333

方式2:

[root@localhost ~]# sed -e "1ahello" -e "1a world" a.txt
111
hello
world
222
333

八、修改文件信息

命令 含义
1s/old/new/ 替换第1行内容old为new
1,10s/old/new/ 替换1行到10行的内容old为new
1,+5s/old/new/ 替换1行到6行的内容old为new
/pattern1/s/old/new/ 替换匹配pattern1的内容old为new
/pattern1/,/pattern2/s/old/new/ 替换匹配到pattern1的行直到
匹配到pattern2的所有行内容old为new
10,/pattern1/s/old/new/ 替换第10行直到匹配到pattern1的所有行内容old为new

有如下文件内容

[root@localhost ~]# cat a.txt
aaa
bbb
ccc
aaa hello aaa AAA
world bbb bbb
ddd

匹配到aaa替换为AAA, 默认只匹配每行的第一次出现

[root@localhost ~]# sed "s/aaa/AAA/" a.txt
AAA
bbb
ccc
AAA hello aaa AAA
world bbb bbb
ddd
[root@localhost ~]# 

替换每行的所有aaa为AAA g参数

[root@localhost ~]# sed "s/aaa/AAA/g" a.txt
AAA
bbb
ccc
AAA hello AAA AAA
world bbb bbb
ddd
[root@localhost ~]# 

替换指定行,例如只替换第四行

[root@localhost ~]# sed "4s/aaa/AAA/g" a.txt
aaa
bbb
ccc
AAA hello AAA AAA
world bbb bbb
ddd

替换第一行到第四行

[root@localhost ~]# sed "1,4s/aaa/AAA/g" a.txt
AAA
bbb
ccc
AAA hello AAA AAA
world bbb bbb
ddd

替换第一行的aaa,或第五行的bbb

[root@localhost ~]# sed "1s/aaa/AAA/g;5s/bbb/BBB/g" a.txt
AAA
bbb
ccc
aaa hello aaa AAA
world BBB BBB
ddd

2. 高级信息替换方式:后项引用前项内容

1.例如将123456输出为12,34,56

[root@localhost ~]# echo 123456 |  sed -r "s/(..)(..)(..)/1,2,3/"
12,34,56

2.将ip地址取出来

[root@localhost ~]# ifconfig ens32 | sed -nr '2s/.*inet (.*) net.*/1/p'
10.0.0.100 

[root@localhost ~]# ifconfig ens32 | grep "inet " | awk '{print $2}'
10.0.0.100

3.批量修改扩展名
补充:将1行多列内容显示为多行1列

[root@localhost ~]# ls | xargs -n1 
1.txt
2.txt
3.txt
4.txt
5.txt

将文件后缀名.txt批量 修改为.jpg

方式1.
[root@localhost ~]# touch {1..5}.txt # 先创建5个txt文件
[root@localhost ~]# ll
总用量 0
-rw-r--r--. 1 root root 0 731 17:55 1.txt
-rw-r--r--. 1 root root 0 731 17:55 2.txt
-rw-r--r--. 1 root root 0 731 17:55 3.txt
-rw-r--r--. 1 root root 0 731 17:55 4.txt
-rw-r--r--. 1 root root 0 731 17:55 5.txt

[root@localhost ~]# ls | sed  -r 's/(.*).(.*)/mv 1.2 1.jpg/' | bash

[root@localhost ~]# ll
总用量 0
-rw-r--r--. 1 root root 0 731 17:55 1.jpg
-rw-r--r--. 1 root root 0 731 17:55 2.jpg
-rw-r--r--. 1 root root 0 731 17:55 3.jpg
-rw-r--r--. 1 root root 0 731 17:55 4.jpg
-rw-r--r--. 1 root root 0 731 17:55 5.jpg
[root@localhost ~]


方式2
[root@manager tmp]# ls |xargs -n1 | sed  's/txt/jpg/'

方式3
[root@manager tmp]# ls | sed  's/txt/jpg/g'

批量生成用户

[root@localhost ~]# echo {1a,2b,3c} | xargs -n1 | sed -nr 's/(.*)/user1/p'
user1a
user2b
user3c

[root@localhost ~]# echo {1a,2b,3c} | xargs -n1 | sed -nr 's/(.*)/useradd user1/p'|bash

补充:备份文件可以这么写

[root@localhost ~]# cp /etc/yum.repos.d/CentOS-Base.repo{,.bak}

还原
[root@localhost ~]# cp /etc/yum.repos.d/CentOS-Base.repo{.bak,}

忽略大小写

[root@localhost ~]# cat a.txt  # 显示a.txt文件内容 
aaa
AAA aaa AAA aaa
AAA

[root@localhost ~]# sed -n '/aaa/p' a.txt  # 过滤包含aaa的行
aaa
AAA aaa AAA aaa

[root@localhost ~]# sed -n '/aaa/Ip' a.txt  # 忽略大小写
aaa
AAA aaa AAA aaa
AAA
[root@localhost ~]# sed -n '/aaa/Ip' a.txt 

-c的用法
将指定的行,替换为指定的内容

[root@localhost ~]# sed '3ctest' a.txt  # 将第三行替换为test
aaa
AAA aaa AAA aaa
test

查找当前目录下大小为0的文件删除他们

[root@localhost ~]# find ./ -size 0 | sed -r 's/(.*)/rm -rf 1/p'|bash

[root@localhost ~]# sed  's/love/LOVE/gi' a.txt  # (gi是匹配一行中所有单词和忽略大小写)
111LOVE
LOVE22222
333LOVE3333
444LOVE
555sfsfs1LOVE1111fLOVEdsff
[root@localhost ~]# 
[root@localhost ~]# sed  '/^love/s/love/LOVE/gi' a.txt #只处理匹配到的行,以love开头,其他行不处理原样输入屏幕
111love
LOVE22222
333love3333
444love
555sfsfs1love1111flovedsff

[root@localhost ~]# sed  '1,3s/love/LOVE/gi' a.txt # 只把13行替换成大写
111LOVE
LOVE22222
333LOVE3333
444love
555sfsfs1love1111flovedsff
[root@localhost ~]# 
[root@localhost ~]# sed  '/^love/s/love/LOVE/gi' a.txt # 只替换以love开头的
111love
LOVE22222
333love3333
444love
555sfsfs1love1111flovedsff

[root@localhost ~]# sed  's/^love/LOVE/gi' a.txt # 同上
111love
LOVE22222
333love3333
444love
555sfsfs1love1111flovedsff
[root@localhost ~]# 
[root@localhost ~]# head -3 a.txt | sed 's/love/LOVE/gi' # 只处理文件的前三行
111LOVE
LOVE22222
333LOVE3333

3. 示例

1.修改passwd文件第1行中第一个root为ROOT
[root@manager tmp]# sed '1s/root/ROOT/' /etc/passwd

2.修改passwd文件中第5行到第10行中所有的/sbin/nologin为/bin/bash
[root@manager ~]# sed -n '5,10s/sbin/nologin/bin/bash/p' /etc/passwd
[root@manager ~]# sed -n '5,10s#/sbin/nologin#/bin/bash#p' /etc/passwd


3.修改passwd文件中匹配到/sbin/nologin的行,将匹配到行中的login为该大写的LOGIN
[root@manager ~]# sed -n "//sbin/nologin/s#login#LOGIN#pg" /etc/passwd


4.修改SELINUX=enforcing修改为SELINUX=disabled(可以使用c替换方式)
[root@manager ~]# sed '/SELINUX=/c SELINUX=disabled' /etc/selinux/config

5.将nginx.conf配置文件添加注释。
[root@manager ~]# sed 's/^/#/' /etc/nginx/nginx.conf

6,提取网卡ip
[root@manager ~]# ifconfig eth0 | sed -nr '2s/.*inet (.*) net.*/1/p'
10.0.0.99 

九、 应用

9.1 分析Ansible主机清单

[root@manager ~]# cat inventory.sh 
function get_host_group_name(){

	host_group_name=$(sed -rn 's#[(.*)]#1#p' hosts )
	echo $host_group_name
}



function get_host_total(){

	total=$(sed -rn '/['$1']/,/[/p' hosts | sed -rn '/([|^$)/!p' | wc -l)
	echo $total
}


function get_host_list(){
	host_list=$(sed -rn '/['$1']/,/[/p' hosts | sed -rn '/([|^$)/!p' | xargs )
	echo $host_list
}



for host in $(get_host_group_name)
do
	echo "主机名称:$host 共有$(get_host_total $host)台主机,主机分别是:$(get_host_list $host)"
done



[root@manager ~]# sh inventory.sh 
主机名称:routes 共有1台主机,主机分别是:172.16.1.200
主机名称:dnsservers 共有2台主机,主机分别是:172.16.1.91 172.16.1.92
主机名称:lbservers 共有2台主机,主机分别是:172.16.1.3 172.16.1.4
主机名称:proxyservers 共有2台主机,主机分别是:172.16.1.5 172.16.1.6
主机名称:webservers 共有3台主机,主机分别是:172.16.1.7 172.16.1.8 172.16.1.9
主机名称:mysqlservers 共有1台主机,主机分别是:172.16.1.51
主机名称:redisservers 共有1台主机,主机分别是:172.16.1.41
主机名称:nfsservers 共有1台主机,主机分别是:172.16.1.32
主机名称:rsyncservers 共有1台主机,主机分别是:172.16.1.31
主机名称:test 共有1台主机,主机分别是:172.16.1.99

9.2 分析mysql配置文件

需求:处理一个MySQL配置文件的 my.cnf文件。
1.输文件中有几个段,一对 [ ] 为一段。
2.针对每个段统计配置文件参数总个数。

储备知识
[root@manager ~]# sed -rn '/[.*]/p' my.cnf 
[client]
[server]
[mysqld]
[mysqld_safe]
[embedded]
[mysqld-5.0]

# 获取所有名称
[root@manager ~]# sed -rn '/[.*]/p' my.cnf  | sed -r 's/[(.*)]/1/'
client
server
mysqld
mysqld_safe
embedded
mysqld-5.0


[root@manager ~]# sed -rn '/[.*]/p' my.cnf | wc -l
6


[root@manager ~]# sed -rn '/[embedded]/,/^#/p' my.cnf | sed -rn '/(^[|^#|^$)/!p'
gtid_mode=on
enforce_gtid_consistency=1
log_slave_updates
slave-rows-search-algorithms='INDEX_SCAN,HASH_SCAN'
binlog_format=row
binlog_checksum=1
relay_log_recovery=1
relay-log-purge=1
[root@manager ~]# cat mysql_config.sh 
# 获取所有名称
function get_name(){
	name_list=$(sed -rn '/[.*]/p' my.cnf  | sed -r 's/[(.*)]/1/')
	echo $name_list
}

# 获取每个名称对应的配置参数总数
function get_total(){
	total=$(sed -rn '/['$1']/,/^#/p' my.cnf | sed -rn '/(^[|^#|^$)/!p'| wc -l)
	echo $total
}

echo "总共有$(sed -rn '/[.*]/p' my.cnf | wc -l)段"

for i in $(get_name)
do
	echo "$i段,参数总个数为:$(get_total $i)"
done



[root@manager ~]# sh mysql_config.sh 
总共有6段
client段,参数总个数为:2
server段,参数总个数为:12
mysqld段,参数总个数为:12
mysqld_safe段,参数总个数为:7
embedded段,参数总个数为:8
mysqld-5.0段,参数总个数为:9