Sed简介
SED是一个非交互式文本编辑器,它可对文本文件和标准输入进行编辑,标准输入可以来自键盘输入、文本重定向、字符串、变量,甚至来自于管道的文本,与VIM编辑器类似,它一次处理一行内容,Sed可以编辑一个或多个文件,简化对文件的反复操作、编写转换程序等。
Sed命令的原理:在处理文本时把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),紧接着用SED命令处理缓冲区中的内容,处理完成后把缓冲区的内容输出至屏幕或者写入文件。逐行处理直到文件末尾,然而如果打印在屏幕上,实质文件内容并没有改变,除非你使用重定向存储输出或者写入文件。
Sed语法参数
参数格式为:
sed [-Options] [‘Commands’] filename;
sed工具默认处理文本,文本内容输出屏幕已经修改,但是文件内容其实没有修改,需要加-i参数即对文件彻底修改;
-e<script>或--expression=<script> 以选项中指定的script来处理输入的文本文件。
-f<script文件>或--file=<script文件> 以选项中指定的script文件来处理输入的文本文件。
-h 或--help 显示帮助。
-n或--quiet或--silent 仅显示script处理后的结果。
-r 使用扩展正则表达式
x #x为指定行号;
x,y #指定从x到y的行号范围;
/pattern/ #查询包含模式的行;
/pattern/,/pattern/ #查询包含两个模式的行;
/pattern/,x #从与pattern的匹配行到x号行之间的行;
x,/pattern/ #从x号行到与pattern的匹配行之间的行;
x,y! #查询不包括x和y行号的行;
r #从另一个文件中读文件;
w #将文本写入到一个文件;
y #变换字符;
q #第一个模式匹配完成后退出;
l #显示与八进制ASCII码等价的控制字符;
{} #在定位行执行的命令组;
p #打印匹配行;
= #打印文件行号;
a #在定位行号之后追加文本信息;
i #在定位行号之前插入文本信息;
d #删除定位行;
c #用新文本替换定位文本;
s #使用替换模式替换相应模式;
& #引用已匹配字符串
first~step #步长,每 step 行,从第 first 开始
$ #匹配最后一行
/regexp/ #正则表达式匹配行
number #只匹配指定行
addr1,addr2 #开始匹配 addr1 行开始,直接 addr2 行结束
addr1,+N #从 addr1 行开始,向后的 N 行
addr1,~N #从 addr1 行开始,到 N 行结束
Sed实例练习(模式空间)
示例文本:
[root@localhost ~]# cat test.txt
www.test.net
www.baidu.com
www.taobao.com
www.sina.com
old old old
new new new
替换test.txt文本中old为new:
sed 's/old/new/g' test.txt
打印test.txt文本第一行至第三行:
sed -n '1,3p' test.txt
打印test.txt文本中第一行与最后一行:
sed -n '1p;$p' test.txt
删除test.txt第一行至第三行、删除匹配行至最后一行:
sed '1,3d' test.txt
sed '/test/,$d' test.txt
删除test.txt最后6行及删除最后一行:
for i in `seq 1 6`;do sed -i '$d' test.txt ;done
sed '$d' test.txt
删除test.txt最后一行:
sed '$d' test.txt
在test.txt查找test所在行,并在其下一行添加word字符,a表示在其下一行添加字符串:
sed '/test/aword' test.txt
在test.txt查找test所在行,并在其上一行添加word字符,i表示在其上一行添加字符串:
sed '/test/i word' test.txt
在test.txt查找以test结尾的行尾添加字符串word,$表示结尾标识,&在Sed中表示添加:
sed 's/test$/& word/g' test.txt
在test.txt查找www的行,在其行首添加字符串word,^表示起始标识,&在Sed中表示添加:
sed '/www/s/^/& word/' test.txt
多个sed命令组合,使用-e参数:
sed -e '/www.jd.com/s/^/&1./' -e 's/www.jd.com$/&./g' test.txt
多个sed命令组合,使用分号“;”分割:
sed -e '/www.jd.com/s/^/&1./;s/www.jd.com$/&./g' test.txt
Sed读取系统变量,变量替换:
TEST=WWW.test.NET
Sed “s/www.jd.com/$TEST/g” test.txt
修改Selinux策略enforcing为disabled,查找/SELINUX/行,然后将其行enforcing值改成disabled、!s表示不包括SELINUX行:
sed -i '/SELINUX/s/enforcing/disabled/g' /etc/selinux/config
sed -i '/SELINUX/!s/enforcing/disabled/g' /etc/selinux/config
去除空格httpd.conf文件空行或者是#号开头的行
sed '/^#/d;/^$/d' /etc/httpd/conf/httpd.conf
打印是把匹配的打印出来,删除是把匹配的删除,删除只是不用-n 选项
IP 加单引号
echo '10.10.10.1 10.10.10.2 10.10.10.3' |sed -r 's/[^ ]+/"&"/g'
"10.10.10.1" "10.10.10.2" "10.10.10.3"
分组() 匹配
对 1-4 行的 html 进行替换
示例文件:
http://www.baidu.com/index.html
http://www.baidu.com/1.html
http://post.baidu.com/index.html
http://mp3.baidu.com/index.html
http://www.baidu.com/3.html
http://post.baidu.com/2.html
tail /etc/services | sed '1,4 s/html/txt/'
http://www.baidu.com/index.txt
http://www.baidu.com/1.txt
http://post.baidu.com/index.txt
http://mp3.baidu.com/index.txt
http://www.baidu.com/3.html
http://post.baidu.com/2.html
分组使用,在第2列后面添加 test
tail /etc/services |sed -r 's/(.*) (.*)(#.*)/12test 3/'
3gpp-cbsp 48049/tcp test # 3GPP Cell Broadcast Service
isnetserv 48128/tcp test # Image Systems Network Services
isnetserv 48128/udp test # Image Systems Network Services
blp5 48129/tcp test # Bloomberg locator
blp5 48129/udp test # Bloomberg locator
com-bardac-dw 48556/tcp test # com-bardac-dw
com-bardac-dw 48556/udp test # com-bardac-dw
iqobject 48619/tcp test # iqobject
iqobject 48619/udp test # iqobject
第一列是第一个小括号匹配,第二列第二个小括号匹配,第三列一样。将不变的字符串匹配分组,再通过数字按分组顺序反向引用。
基本正则表达式中支持分组,而在扩展正则表达式中,分组的功能更加强大,也可以说才是真正的分组
():分组,后面可以使用1 2 3...引用前面的括号分组。
处理以下文件内容,将域名取出并进行计数排序,如处理:
http://www.baidu.com/index.html
http://www.baidu.com/1.html
http://post.baidu.com/index.html
http://mp3.baidu.com/index.html
http://www.baidu.com/3.html
http://post.baidu.com/2.html
得到如下结果:
域名的出现的次数域名
[root@localhost shell]# cat file | sed -e ' s/http:////' -e ' s//.*//' | sort | uniq -c | sort -rn
3 www.baidu.com
2 post.baidu.com
1 mp3.baidu.com
[root@codfei4 shell]# awk -F/ '{print $3}' file |sort -r|uniq -c|awk '{print $1" ",$2}'
3 www.baidu.com
2 post.baidu.com
1 mp3.baidu.com*
将协议与端口号位置调换
tail /etc/services |sed -r 's/(.*)(<[0-9]+>)/(tcp|udp)(.*)/13/24/'
3gpp-cbsp tcp/48049 # 3GPP Cell Broadcast Service
isnetserv tcp/48128 # Image Systems Network Services
isnetserv udp/48128 # Image Systems Network Services
blp5 tcp/48129 # Bloomberg locator
blp5 udp/48129 # Bloomberg locator
com-bardac-dw tcp/48556 # com-bardac-dw
com-bardac-dw udp/48556 # com-bardac-dw
iqobject tcp/48619 # iqobject
iqobject udp/48619 # iqobject
matahari tcp/49000 # Matahari Broker
字符位置调换
替换 x 字符为大写:
# echo "abc cde xyz" |sed -r 's/(.*)x/1X/'
abc cde Xyz
456 与 cde 调换:
# echo "abc:cde;123:456" |sed -r 's/([^:]+)(;.*:)([^:]+$)/321/'
abc:456;123:cde
注释匹配行后的多少行
[root@localhost ~]# sed '/5/,+3s/^/@/' 1.txt
1
2
3
4
@5
@6
@7
@8
9
10
注释指定多行
[root@localhost ~]# sed -r 's/^3|^5|^7/#&/' 1.txt
1
2
#3
4
#5
6
#7
8
9
10
去除开头和结尾空格或制表符
echo " 1 2 3 " |sed 's/^[ ]*//;s/[ ]*$//'
1 2 3
Sed实例练习(保留空间)
Sed之所以能以行为单位进行修改文本或者编辑文本,主要原因是因为它使用了两个空间:一个是活动的“模式空间”,另一个是辅助作用的“保留空间”.
模式空间:可以想象成工程里面的流水线,所有的数据读取过来之后就直接在上面进行工作。
保留空间:可以想象成是仓库,我们在进行数据处理的时候,会把数据读取到保留空间中,作为数据的暂存区域,需要时再进行调出。
SED高级命令可以分为三种功能:
N、D、P:处理多行模式空间的问题;
H、h、G、g、x:将模式空间的内容放入保留空间以便接下来的编辑;
:、b、t:在脚本中实现分支与条件结构(标签)。
n #读取下一个输入行,用下一个命令处理新的行;
N #将当前读入行的下一行读取到当前的模式空间。
d #删除模式空间的所有内容,开始下一个循环
D #删除模式空间的第一行,开始下一个循环;
p #打印当前模式空间的所有内容;
P #打印模式空间的第一行,开始下一个循环
h #将模式缓冲区的文本复制到保持缓冲区;
H #将模式缓冲区的文本追加到保持缓冲区;
x #互换模式缓冲区和保持缓冲区的内容;
g #将保持缓冲区的内容复制到模式缓冲区;
G #将保持缓冲区的内容追加到模式缓冲区。
在test.txt每行后加入空行,也即每行占永两行空间,每一行后边插入一行空行、两行空行及前三行每行后插入空行:
sed '/^$/d;G' test.txt #删除的是匹配的条件行,留下的是模式空间处理的行,G参数是将保持空间内的行追加到模式空间去(保持空间默认是空的)。
sed '/^$/d;G;G' test.txt #后面跟2个G 是将G保持空间内的两行追加到模式空间
sed '/^$/d;1,3G;' test.txt #1,3G 是只将追加3行到模式空间
将test.txt偶数行删除及隔两行删除一行:
sed 'n;d' test.txt #n参数将输入行的下一行显示出来,而后面的d 正好会将显示的行进行删除,测试“sed –n ‘n;p’ file ”
sed 'n;n;d' test.txt, #2个n参数将输入的的下两行显示出来,正好使用d都删除,形成隔两行删一行的效果。
在test.txt匹配行前一行、后一行插入空行以及同时在匹配前后插入空行:
sed '/test/{x;p;x;}' test.txt #x参数是让模式空间和保持空间互相交换,匹配test时,将模式空间内的数据换成了保持空间内的数据,保持空间默认是空的,所以前一行会变成空行,然后将空行打印出来
sed '/test/G' test.txt #G参数配置上之后将保持空间的内容追加到模式空间去。
sed '/test/{x;p;x;G;}' test.txt #综合以上两个参数配置。
在test.txt每行前加入顺序数字序号、加上制表符 及.符号:
sed = test.txt| sed 'N;s/
/ /' # “=”等号打印当前行号码。N参数是配置将当前读入行的下一行读取到当前的模式空间,就是把下一行读到当前的模式空间来,利用s替换换行符,形成数字顺序效果。
sed = test.txt| sed 'N;s/
/ /' #案例同上
sed = test.txt| sed 'N;s/
/./' #案例同上
删除test.txt行前和行尾的任意空格:
sed 's/^[ ]*//;s/[ ]*$//' test.txt #s替换 删除行前
打印test.txt关键词old与new之间的内容:
sed -n '/old/,/new/'p test.txt #-n参数可以打印匹配的条件内容行
打印及删除test.txt最后两行:
sed '$!N;$!D' test.txt
N 读取下一行并追加输入到模式空间
D 删除模式空间的第一行,开始下一个循环
#读取1,$!条件满足(不是尾行,#N前加$!表示末尾行不执行N),执行N命令,读取下一行并追加输入到模式空间,得出1
2。执行$!D,不是最后一行,所以执行D,删除模式空间的第一行,开始下一个循环,模式空间由1
2成了2。读取第二行,执行 N 命令,此时模式空间是 3
4,执行 D 命令删除模式空间第一行 3,剩余4,直到执行N读入第5行,$!条件不满足(不是尾行),不执行N命令:继续读入6行,这里模式空间为:5
6,$!D,因为是最后一行,所以不执行D,控制流到达脚本底部,输出模式空间的内容
sed 'N;$!P;$!D;$d' test.txt
P 打印模式空间的第一行
N 读取下一行并追加输入到模式空间
D 删除模式空间的第一行,开始下一个循环
d 删除匹配的行
#读取第一行,执行N,此时得出1
2,P打印从开始到第一个
的内容,执行$!D,不是最后一行,所以执行D,删除模式空间的第一行,开始下一个循环,模式空间由1
2成了2,$d 是因为不是末行所以不执行,读取第二行,执行 N 命令,此时模式空间是 3
4,执行 P 命令显示模式空间第一行 3,执行D,删除模式空间的第一行,剩余4,读取第5行,执行N,将第6行进行读取,执行$!P,因为是最后一行不执行P,执行$!D,因为是最后一行,不执行D,最后执行$d,删除模式空间5/6行。
合并上下两行,也即两行合并:
sed '$!N;s/
/ /' test.txt #N前加$!表示末尾行不执行N
sed 'N;s/
/ /' test.txt
Sed中标签使用(: 、b 和 和 t )
标签可以控制流,实现分支判断。
: lable name 定义标签
b lable 跳转到指定标签,如果没有标签则到脚本末尾
t lable 跳转到指定标签,前提是 s///命令执行成功
将换行符替换成逗号
方法 1:
seq 6 |sed 'N;s/
/,/'
1,2
3,4
5,6
这种方式并不能满足我们的需求,每次 sed 读取到模式空间再打印是新行,替换
也只能对 N 命令,追加后的 1
2 这样替换。
这时就可以用到标签了:
seq 6 |sed ':a;N;s/
/,/;b a'
1,2,3,4,5,6
看看这里的标签使用,:a 是定义的标签名,b a 是跳转到 a 位置。
sed 读取第一行 1,N 命令读取下一行 2,此时模式空间是 1
2$,执行替换,此时模式空间是1,2$,执行 b 命令再跳转到标签 a 位置继续执行 N 命令,读取下一行 3 追加到模式空间,此时模式空间是 1,2
3$,再替换,以此类推,不断追加替换,直到最后一行 N 读不到下一行内容退出。
方法 2:
seq 6 |sed ':a;N;$!b a;s/
/,/g'
1,2,3,4,5,6
先将每行读入到模式空间,最后再执行全局替换。$!是如果是最后一行,则不执行 b a 跳转,最后执行全局替换。
seq 6 |sed ':a;N;b a;s/
/,/g'
1
2
3
4
5
6
可以看到,不加$!是没有替换,因为循环到 N 命令没有读到行就退出了,后面的替换也就没执行。
每三个数字加个一个逗号
# echo "123456789" |sed -r 's/([0-9]+)([0-9]+{3})/1,2/'
123456,789
# echo "123456789" |sed -r ':a;s/([0-9]+)([0-9]+{3})/1,2/;t a'
123,456,789
# echo "123456789" |sed -r ':a;s/([0-9]+)([0-9]+{2})/1,2/;t a'
1,23,45,67,89
执行第一次时,替换最后一个,跳转后,再对 123456 匹配替换,直到匹配替换不成功,不执行 t 命令。
忽略大小写匹配(I )
# echo -e "a
A
b
c" |sed 's/a/1/Ig'
1
1
b
c
获取总行数(# )
seq 10 |sed -n '$='
Sed 脚本使用编写方法
<1>从文件读入命令
sed -f sed.sh
sed.sh文件内容:
s/root/yerik/p
s/bash/csh/p
<2>直接运行脚本 ./sed.sh /etc/passwd
#!/bib/sed -f
s/root/yerik/p
s/bash/csh/p
Sed 扩展练习高级替换
1,删除文件每行的第一个字符。
sed -n 's/^.//gp' /etc/passwd
sed -nr 's/(.)(.*)/2/p' /etc/passwd
sed -r 's/^.//g' test.txt
2,删除文件每行的第二个字符。
sed -nr 's/(.)(.)(.*)/13/p' /etc/passwd
sed -r 's/(^.)(.)/1/g' test.txt
3,删除文件每行的最后一个字符。
sed -nr 's/.$//p' /etc/passwd
sed -nr 's/(.*)(.)/1/p' /etc/passwd
sed -r 's/(.)$//g' test.txt
4,删除文件每行的倒数第二个字符。
sed -nr 's/(.*)(.)(.)/13/p' /etc/passwd
sed -r 's/(.)(.)$/2/g' test.txt
5,删除文件每行的第二个单词。
sed -nr 's/([^a-Z]*)([a-Z]+)([^a-Z]+)([a-Z]+)(.*)/1235/p' /etc/passwd
sed -r 's/([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)(.*)/1245/' test.txt
6,删除文件每行的倒数第二个单词。
sed -nr 's/(.*)([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]*)/12456/p' /etc/passwd
sed -r 's/([a-Z]+)([^a-Z])([a-Z]+)$/23/g' test.txt
7,删除文件每行的最后一个单词。
sed -nr 's/(.*)([^a-Z]+)([a-Z]+)([^a-Z]*)/124/p' /etc/passwd
sed -r 's/([a-Z]+)$//g' test.txt
sed -r 's/(.*)([^a-Z]+)([a-Z]+)/12/' test.txt
8,交换每行的第一个字符和第二个字符。
sed -nr 's/(.)(.)(.*)/213/p' /etc/passwd
sed -r 's/(.)(.)/21/' test.txt
9,交换每行的第一个字符和第二个单词
sed -r 's/(^.)([^a-Z]*)([a-Z]+)([^a-Z]+)([a-Z]+)/52341/' test.txt
9,交换每行的第一个单词和第二个单词。
sed -nr 's/([^a-Z]*)([a-Z]+)([^a-Z]+)([a-Z]+)(.*)/14325/p' /etc/passwd
10,交换每行的第一个单词和最后一个单词。
sed -r 's/([a-Z]+)([^a-Z]+)(.*)([^a-Z]+)([a-Z]+)/52341/' test.txt
11,删除一个文件中所有的数字。
sed 's/[0-9]*//g' test.txt
12,删除每行开头的所有空格。
sed -n 's/^ *//p' /etc/passwd
sed -nr 's/( *)(.*)/2/p' test.txt
sed 's/^ *//' test.txt
13,用制表符替换文件中出现的所有空格。
sed -n 's/ / /gp' test.txt
sed -r 's/( +)/ /g' test.txt
sed 's/ / /g' test.txt
14,把所有大写字母用括号()括起来。
sed -nr 's/([A-Z])/(&)/gp' test.txt
sed -n 's/[A-Z]/(&)/gp' test.txt
15,打印每行3次。
sed 'p;p' test.txt
sed -n 'p;p;p' test.txt
16,隔行删除。
sed -n '1~2p' test.txt
sed '1d;n;d' ww.txt
sed '1~2d' ww.txt
sed '0~2d' ww.txt
17,把文件从第22行到第33行复制到第44行后面。
sed '1,21h;22h;23,33H;44G' pass
cat -n /etc/passwd | sed '22h;23,33H;44G'
18,把文件从第22行到第33行移动到第44行后面。
sed '22{h;d};23,33{H;d};44G' pass
cat -n /etc/passwd | sed '22{h;d};23,33{H;d};44G'
19,只显示每行的第一个单词。
sed -nr 's/([^a-Z]*)([a-Z]+)([^a-Z]+)(.*)/2/p' /etc/passwd
sed -r 's/([a-Z]+)(.*)/1/' test.txt
20,打印每行的第一个单词和第三个单词。
sed -nr 's/([^a-Z]*)([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)(.*)/2--4/p' /etc/passwd
sed -r 's/([^a-Z]*)([a-Z]+)([^a-Z]+)([a-Z]+)([^a-Z]+)([a-Z]+)(.*)/26/' test.txt
21,将格式为 mm/yy/dd 的日期格式换成 mm;yy;dd
date +%m/%Y/%d |sed -n 's#/#;#gp'
22, 逆向输出
cat a.txt
ABC
DEF
XYZ
sed '1!G;h;$!d' a.txt
输出样式变成
XYZ
DEF
ABC
Sed 作业练习:
- 把/etc/passwd 复制到/root/test.txt,用sed打印所有行
- 打印test.txt的3到10行
- 打印test.txt 中包含 ‘root’ 的行
- 删除test.txt 的15行以及以后所有行
- 删除test.txt中包含 ‘bash’ 的行
- 替换test.txt 中 ‘root’ 为 ‘toor’
- 替换test.txt中 ‘/sbin/nologin’ 为 ‘/bin/login’
- 删除test.txt中5到10行中所有的数字
- 删除test.txt 中所有特殊字符(除了数字以及大小写字母)
- 把test.txt中第一个单词和最后一个单词调换位置
- 把test.txt中出现的第一个数字和最后一个单词替换位置
- 把test.txt 中第一个数字移动到行末尾
- 在test.txt 20行到末行最前面加 ‘aaa:’
sed习题答案
1. /bin/cp /etc/passwd /root/test.txt ; sed -n '1,$'p test.txt
2. sed -n '3,10'p test.txt
3. sed -n '/root/'p test.txt
4. sed '15,$'d test.txt
5. sed '/bash/'d test.txt
6. sed 's/root/toor/g' test.txt
7. sed 's#sbin/nologin#bin/login#g' test.txt
8. sed '5,10s/[0-9]//g' test.txt
9. sed 's/[^0-9a-zA-Z]//g' test.txt
10. sed 's/(^[a-Z]*)([^a-Z].*)([^a-Z])([a-Z]*$)/4231/' test.txt
11. sed 's#([^0-9][^0-9]*)([0-9][0-9]*)([^0-9].*)([^a-zA-Z])([a-zA-Z][a-zA-Z]*$)#15342#' test.txt
12. sed 's#([^0-9][^0-9]*)([0-9][0-9]*)([^0-9].*$)#132#' test.txt
13. sed '20,$s/^.*$/aaa:&/' test.txt