文章目录
- sed基本结构
- 选项
- -n(静默模式)
- -e、;(多点编辑,多条命令)
- -f(指定脚本文件)
- -i(直接修改文件与备份)
- -E(扩展正则表达式)
- 常用动作
- p(print,打印)
- i(插入内容)与a(追加内容)
- c、y(替换)
- d(delete,删除)
- s(替换)
- 其他动作
- l(小写L,打印特殊字符)
- r(在匹配行后插入指定文件内容)
- w(将匹配行写入指定文件)
- 退出、下一行
- 取反(!)
- 模式空间与暂存区
- 其他示例
- 一些需要注意的点
sed基本结构
sed 选项 [模式1][,模式2] [动作][flag] filename
模式指的是Addresses,有的朋友也会把它直译为地址。
sed的格式非常灵活,导致有时候可读性不好,不信看:
sed '1croot' sed.txt
上面这个命令简单吧,如果对sed不熟悉,晃眼可能还得愣2秒才能反应过来。
所以,我们想要一眼就可能知道sed在干什么,就一定要清楚sed结构。
我们先来看一个简单的例子,说明一下结构。
# -n是选项表示使用静默模式, '2,5p'是动作表示打印2到5行 sed.txt是要处理文件
sed -n '2,5p' sed.txt
上面这个命令也非常简单,但是它几乎包含了sed的全部结构,我们在它的基础上稍微变一下,还能一眼看穿吗?
sed '1~2p' sed.txt
sed '1,5s/aa/zzzz/g' sed.txt
sed '2s/aa/AA/2' sed.txt
sed '/aa/s/aa/AA/2' sed.txt
sed '/^luck/,+3s/luck/LUCK/g' sed.txt
sed -n '/2024-10-13 06:52:48/,/2024-10-13 06:53:21/w app.log.bak' app.log
我们只要理解结构,就会发现万变不离其中:
模式部分,我们总结一下:
模式可以包含2部分,表示匹配的区间,模式有下面几种格式:
模式格式 | 示例说明 |
---|---|
n,m | sed ‘3,5p’ sed.txt,表示只打印区间3-5行 |
n | sed ‘5p’ sed.txt,表示只打印第5行,可以看做是sed ‘5,5p’ sed.txt的简写 |
n,+m | sed ‘3,+5p’ sed.txt,表示只打印区间3-8行,可以看做是sed ‘3,8(3+5)p’ sed.txt |
/xxx/ | sed ‘/luck/’ sed.txt,表示包含luck的行 |
/xxx/,/yyy/ | sed ‘/luck/lovely/’ sed.txt,表示打印区间,从包含luck的行开始,到包含lovely的行结束 |
模式都是上面的组合,如:
- /xxx/,10
- /xxx/,+3
- 3,/xxx/
- /xxx/,/yyyy/
- 3,5
- 3,+5
- 3,$:3到最后一行
- /xxx/,~3
- 1,~3
- 1~3:这个特殊,可以看做初始值1,步长3直到sed行结尾,注意和上2个的区别(文章最后)
选项
选项 | 说明 |
---|---|
-n | slient静默模式,不输出原来的行,通常与p一起使用 |
-e | 多点编辑 |
-f | 指定sed命令脚本,-f filename则可以执行filename内的sed动作 |
-i | 直接修改读取的文件内容,而不是输出print,危险危险 |
-E | 使用扩展表达式 |
-r | 和-E等价,表示使用扩展正则表达式 |
关于扩展正则表达式,可以参考:Linux grep命令详解(多图、多示例)
-n(静默模式)
sed '2,5p' sed.txt
sed -n '2,5p' sed.txt
-e、;(多点编辑,多条命令)
# 打印2到5号,打印每行行号
sed -n -e '2,5p' -e '=' sed.txt# 除了-e还可以使用分号(;),等价于上一个命令
sed -n '2,5p;=' sed.txt# 打印第4行和最后一行
sed -n '4;$p' sed.txt
不知道你有没有发现,-e参数和;指定的命令模式不同,相当于都是独立的指令,如果我们要对同一个模式匹配,做多个动作,怎么办呢?
注意下面2个命令的区别:
# 这相当于是2个指令,按行读取,分别执行2个指令
sed -n '2,5p;=' sed.txt# 这是相当于1个指令,2个动作,对于2到5行这个模式匹配,分别打印和输出行号
sed -n '2,5{p;=}' sed.txt
-f(指定脚本文件)
sed -n -f sed.action sed.txt
sed.action的内容:
2,5p
=
4,6p
-i(直接修改文件与备份)
sed -i -e '2izzzzzz' -e '$ayyyyyy' sed.txt# -i直接修改文件太危险,可以使用-i.bak修改的同时生成备份文件
# -i后面的作为备份文件的后缀,通常习惯使用.bak
sed -i.bak 's/11/zzzzz/' sed.txt
-E(扩展正则表达式)
sed支持正则表达式,和grep一样,可以使用-E参数来说指定使用扩展正则表达式(不用使用\转义)
在//之间的是正则表达式:/正则表达式/
# 打印以bb开头,aa结尾的行
sed -n -E '/^bb.*aa$/p' sed.txt#
sed -n '/a.?/p' sed.txt
sed -n -E '/a.?/p' sed.txt
常用动作
动作 | 说明 |
---|---|
p、P | 打印行 |
s | 替换指定字符串 |
a | 匹配行之后追加行 |
i | 匹配行之前插入行 |
c | 替换指定行 |
d | 删除行 |
p(print,打印)
# 打印第1个包含bb的行到第一个包含11之间的行(包括匹配行)
sed -n '/bb/,/11/p' sed.txt# 打印第1个以bb开头的行以及接下来的2行
sed -n '/^bb/,+2p' sed.txt# 打印包含luck的行
sed -n '/luck/p' sed.txt# 打印包含OOM 或ERROR的行,忽略大小写
sed -n -E '/OOM|ERROR/Ip' sed.txt# 打印匹配行号
sed -n '/luck/=' sed.txt# 打印最后一行
sed -n '$p' sed.txt
# 打印4行到最后1行
sed -n '4,$p' sed.txt# 打印偶数行,n简单的看就是下1行,所以从第1行开始,打印下1行,就是打印的偶数行
# n本质上的操作是把下一行的数据放入模式空间
sed -n 'n;p' sed.txt
# 打印奇数行
sed -n 'p;n' sed.txt# 表示从第2行开始,每隔2(3-1)行打印
sed -n '2~3p' sed.txt# 打印包含yes的行不区分大小写
sed -n '/[Yy][Ee][Ss]/p' sed.txt# 打印长度大于10的行,可以用来过滤代码行超长的行
sed -E -n '/^.{10}/p' sed.txt# 只打印tcp连接
ss | sed -n '/tcp/p'
i(插入内容)与a(追加内容)
- i插入,是在指定行之前insert内容
- a追加,是在指定行之后append内容
sed -e '2,3i 插入i' -e '4,5a 追加a' sed.txtsed -e '2,3i 插入i\n插入i2' -e '4,5a 追加a\n追加a2' sed.txt# 格式要求不高,下面这些都可以
sed -e '2,3i插入i' -e '4,5a追加a' sed.txt
sed -e '2i插入i' -e '5a追加a' sed.txt
sed -e '2i 插入i' -e '5a 追加a' sed.txt
sed -e '2i\插入i' -e '5a\追加a' sed.txt
sed -e '2ii插入i' -e '5aa追加a' sed.txtsed -e '2,3i 插入i' -e '$a new year' sed.txt# 在与#开头的行之后的行添加空行
sed '/^#/a\\' sed.txt
c、y(替换)
- c(change)是用指定内容替换指定行
- y是使用指定字符替换对应字符
# 将第2行替换为zzzzzzzzz
sed '2czzzzzzzzz' sed.txt# 将行中的a全部换为z、b全部换为y,/xxx/yyy/其中xxx和yyy一一对应,长度必须相等
sed 'y/ab/zy/' sed.txt
d(delete,删除)
# 删除2到5行
sed '2,5d' sed.txt
# 删除11开头的行以及其后2行的内容
sed '/^11/,+2d' sed.txt# 删除11开头的行到第4行内容
sed '/^11/,4d' sed.txt# 删除空白行
sed '/^$/d' sed.txt
s(替换)
标志位 | 说明 |
---|---|
\l | 把下个字符转换成小写 |
\L | 把匹配字母转换成小写,直到\U或\E出现 |
\u | 把下个字符转换成大写 |
\U | 把匹配字母转换成大写,直到\L或\E出现 |
\E | 停止以\L或\U开始的大小写 |
g | 表示替换全部 |
i、I | 大写i与小写i,表示忽略大小写 |
& | 表示引用匹配到的内容 |
\n | 表示引用匹配的分组,n为1、2、3…例如\1 \2 |
注意:\n肯定需要-E参数,没有正则匹配不了分组
sed 's/aa/##/I' sed.txt
sed 's/aa/##/i' sed.txt
# 将文件中每一行的aa替换为zz,g表示替换所有
sed -n 's/aa/zz/g' sed.txt
sed -n 's/a./zz/g' sed.txt
# 使用#作为界定符合和上一个命令等价,也可以使用#、:、|、@等作为界定符
sed -n 's#aa#zz#g' sed.txt
# 这里我们使用:做界定符,因为我要替换的内容本身有/
sed 's:/bin:/sbin:g' /etc/passwd
# 否则,如果使用正斜杠/做界定符,如果替换的内容中本身就有/就要使用\转义(\/)
sed 's/\/bin\/bash/\/bin\/sh/' /etc/passwd
# 换成#就清楚多了,3个#将内容分成2部分,第1部分是要匹配的内容,第2部分是要替换的内容
sed 's#/bin/bash#/bin/sh#' /etc/passwd # 可以使用正则表达式
sed -n -E 's/a.?/zz/g' sed.txt# &引用匹配到的内容
sed -n -E 's/^11/这是匹配到的行:&/g' sed.txt# \1 \2 \3...引用分组(小括号中内容)
# 这里是将匹配到的数字前面加上$
sed -E 's/[^0-9]*([0-9]+)[^0-9]*/$\1 /g' sed.txt# 在所有行前加#
sed 's/^/#/g' sed.txt
# 在所有非#开头的行加#
sed 's/^[^#]/#/g' sed.txt
# 删除所有#开头的行
sed '/^#/d' sed.txt# 将所有字母大写
sed -E 's/(.*)/\U\1/g' sed.txt# 将所有单词首字母大写
sed -E 's/(\b[^\s])/\u\1/g' sed.txt# 给每一行加上双引号
sed 's/.*/"&"/' sed.txt# 为每一行添加上#
sed 's/^/#/' sed.txt# 为包含fuck的行的行尾加上----包含敏感词
sed '/fuck/s/$/----包含敏感词/'# 统计文件单词频次,先用sed按空格拆分为行,排序,统计相同的单词数量
sed 's/ /\n/g' sed.txt | sort | uniq -c
其他动作
动作 | 说明 |
---|---|
= | 输出行号 |
w、W | 将匹配的行写入指定文件 |
r | 从文件中读取输入行 |
l | 列出非打印字符 |
q | 结束或退出sed |
l(小写L,打印特殊字符)
这个在处理一些特殊字符的时候非常有用,例如后面我们要说的模式空间测试的时候。
sed -n 'l' sed.txt
sed -n 'l' ascii.txt
r(在匹配行后插入指定文件内容)
# 在sed.txt文件中以11开头的行后插入文件sed-r.txt的文件内容,文件必须使用绝对路径
sed '/^11/ r /home/ubuntu/sed-r.txt' sed.txt
w(将匹配行写入指定文件)
sed '/^11/ w /home/ubuntu/sed-w.txt' sed.txt
退出、下一行
# 打印完第前3行后退出sed
sed '3q' sed.txt# 匹配到luck开头的行,并打印下1行,n是把下1行复制到模式空间
sed '/^luck/{ n; p; }' sed.txt# 匹配到luck开头的行,移动到匹配行的下1行,将这1行的:替换为空格
sed '/^luck/{ n; s/:/ /g; }' sed.txt
取反(!)
取反有2中操作:
- 对模式取反
- 对动作取反
- 2,5!{p}:表示对模式取反,不打印2-5行
- 2,5{!p}:对动作取反,匹配2-5行,但是不打印
- 2,5!p:对模式取反,同1,模式匹配的优先级高,先模式后动作,所以模式匹配就把!给消费掉了
seq 11 1 17 | sed -n '2,5!{p}'
seq 11 1 17 | sed -n '2,5{!p;=}'
seq 11 1 17 | sed -n '2,5!p'
模式空间与暂存区
sed有一个模式空间,还有一个暂存区(存放临时数据),大致处理流程为:
- sed逐行处理,每行内容存在模式空间
- 执行print动作打印到屏幕(除非行被删除或者输出被取消)
- 清空模式空间,读取下一行到模式空间
理解了上面这些内容,才能理解下面这些命令:
动作 | 说明 |
---|---|
h | 将模式空间内容复制到暂存区 |
H | 将模式空间内容追加到暂存区 |
g | 将暂存区内容复制到模式空间,原模式空间内容被覆盖 |
G | 将暂存区内容追加到模式空间,原模式空间内容被保留 |
n | 读取匹配到的行的下1行复制至模式空间,原模式空间内容被覆盖 |
N | 读取匹配到的行的下1行追加至模式空间,原模式空间内容被保留 |
d | 删除模式空间内容 |
D | 删除模式空间开始至\n的内容,对剩余模式空间内容重新执行sed |
x | 交互模式空间与暂存区的内容 |
便于记忆:
- h可以当做holder表示暂存区
- g可以当做global表示模式空间
- n可以当做next line表示下一行
- 小写是覆盖,大写是追加(大内容更多)
# 将所有以11开头的行追加到暂存区,最后追加到最后1行
sed -e '/^11/H' -e '$G' sed.txt
# 匹配到11的行替换为最近匹配到的bb所在的行
sed -e '/bb/h' -e '/11/x' sed.txt
我们再来看一个更复杂一点的,反序输出:
# !取反,1!G表示第1行不将暂存追加到模式空间,其他行还是执行
# $!d,表示最后1行不删除模式空间内容,其他行还是执行
seq 4 | sed '1!G;h;$!d'
大概步骤:
步数 | 模式空间 | 暂存区 | 行结束模式空间 |
---|---|---|---|
1 | 1 | 1 | -(模式空间内容被删除,没有内容输出) |
2 | 2-1(将上1步暂存区的1,追加到模式空间) | 2-1(将前面模式空间的内容拷贝到暂存区) | - |
3 | 3-2-1(将上1步暂存区的2-1,追加到模式空间) | 3-2-1 | - |
4 | 4-3-2-1 | 4-3-2-1 | 4-3-2-1(最后1行的时候模式空间内容不删除,执行print) |
更多示例:
# 删除包含@Test的下一行
sed '/@Test/{n,d}' sed.txt# 把偶数行合并到前一行
sed '{N;s/\n/ /}' sed.txt# 删除多余空行
sed -i.bak '/^$/{N;/\n$/D}' sed.txt
其他示例
查看我们已经有grep了,sed主要用来预处理文件,所以通常是find的最佳搭档。
对find不熟悉的朋友,可以参考:Linux find命令详解与实际使用
find . -name "*.config" -mmin -60 | xargs sed -i 's/127\.0\.0\.1/localhost/g' find . -name "*.log" -mmin -60 -exec sed -E '/ierror|oom/I w /home/unbuntu/log.log' {} \;# 删除空行
sed '/^$/d' sed.txt
# 删除空行及空白行
sed -E '/^$\s*/d' sed.txt
# 把以#开头、空行、空格的行过虑掉
sed '/^#/d; /^$/d ;/^ /d' sed.txt# 删除包含fuck的行
sed '/fuck/d' sed.txt# 打印所有以#开头的数据
sed -n '/^#/p' sed.txt
# 查看nginx代理
sed -n '/proxy_pass/p' nginx.conf
# 查看nginx配置的域名
sed -n '/server_name/p' nginx.conf# 将第一行替换成:#!/bin/bash,例如想将sh换成bash
find . -name "*.sh" | sed -i '1c#!/bin/bash'# 在文件第1放插入:#!/bin/bash
find . -name "*.sh" | sed -i '1i#!/bin/bash'# 将所有以fuck开头的内容向后删掉
sed -E 's/fuck.*//' sed.txt# 在包含aa行的下方插入一行
sed '/aa/a\\' sed.txt
# 在包含@Test行的上方插入一行
sed '/@Test/i\\' FindTest.java# 将指定词组删除
sed -n 's/[fuck,草,屏蔽词]//g' sed.txt
一些需要注意的点
sed命令的$表示最后1行的时候,不是只文件的最后一行,而是指命令处理的最后1行。
这一点和awk的NR变量一样,对awk感兴趣的朋友可以看一下:Linux awk命令详解-参数-选项-内置变量-内置函数-脚本(多图、多示例)
当然,这通常是在多文件的时候才会有区别
sed -n '$p' sed.txt
sed -n '$p' sed.txt num.txt
sed -n -s '$p' sed.txt num.txt
在p动作的时候也可以指定忽略大小写,但是必须用大写i
# 忽略大小写,打印包含aa|AA|Aa|aA的行
sed -n '/aa/Ip' sed.txt
# 小写的i不行
sed -n '/aa/ip' sed.txt
sed -n '/zz/,~2{=;p}' sed.txt
sed -n '1~2{=;p}' sed.txt
sed -n '1,~2{=;p}' sed.txt
最后,命令细节非常多,如有有疑问,最好的方式:man sed