一、Sed简述
Sed是一种流编辑器(stream editor),也是行编辑器。它的处理机制并不是处理文本文件本身,而是每一次把文本逐行读取并载入到内存空间在内存中编辑,然后把结果输出(sed默认不编辑原文件,只对模式空间中的数据做处理结束后并显示结果)。而这个内存空间对sed而言称为“模式空间“、称为模式空间的原因是Sed可以指定只处理指定的行,而这些行也可以像grep做模式过滤的。
二、Sed工作流程
1)当用sed命令对文本进行处理的时候,sed先读取对象的文本文件的第一行到模式空间中。
2)有内容进入“模式空间”时,sed的编辑命令对模式空间中的内容进行编辑操作(修改,替换,删除,追加,显示等等)。
3)模式空间中的内容编辑处理完成之后,sed把此内容通过标准输出(默认为显示器)打印出来,并删除模式空间中的内容。
4)第一行处理结束。从新读取第二行的内容进行处理,直到最后一行。
三、Sed使用语法
Sed的使用其实也不是很麻烦,就是Address+Command来处理指定的文本,同时可以配合选项。如下:
1 |
sed [OPTIONS] '[Address]Command' File |
[OPTIONS]
1 2 3 4 5 |
-n #静默模式,不在默认显示模式空间中的行只显示匹配的行; -i #可以直接修改源文件; -e 'AddCmd' #多点编辑; -f SCIRPT #可以引用脚本来处理文件; -r #使用扩展正则表达式; |
[Address]
1 2 3 4 5 6 |
Startline,Endline #起始行到结束行如1,10;$表示最后一行;$-1表示倒数第二行; /Regexp/ #支持正则表示式; /pattern1/,/pattern2/ #第一次被模式匹配到的行开始到最后一次被模式匹配到的行结束中间所有的行; Linenumber #指定的行; StartLine,+N #从指定的起始行到向后的N行; first-step #指定起始的位置及步长,例如:1~2表示1,3,5; |
Command
1 2 3 4 5 6 7 8 9 |
d #删除指定的行; p #显示符合条件的行; a STRING #在指定的行的下面添加一行内容为STRING; i STRING #在指定的行的上面添加一行内容为STRING; c STRING #把匹配到的行与给定的STRING进行替换; r FILE #在指定的行后面添加一个FILE; w FILE #把指定范围内的行保存到另一个文件中; s/regexp/string/修饰符 #把指定文件中被正则表达式匹配到的内容替换成string,默认只替换每一行中第一次被匹配到的行,全文替换加修饰符; s/regexp//修饰符 #当string中为空时可以删除某一行中的特定字符; |
修饰符:
1 2 3 |
g #表示全局替换; i #忽略字符大小写; p #如果替换成功则打印; |
PS:s###或s@@@跟s///一样都可以用,当有相同字符出现的时候不想转义就可以使用别的分隔符开替代。
四、正则表达式(Regexp)
正则表达式,是一门相对通用的语言。简单说就是:用一系列的规则语法,去匹配,查找,替换等操作字符串,以达到对应的目的;此套规则,就是所谓的正则表达式。各个语言都有各自正则表达式的内置模块,包括Linux系统中sed、awk也都是使用正则表达式。当然Python中也有对正则表达式的支持,对应的就是Python内置的re模块。
正则表达式是由普通字符(例如字符a到z)以及特殊字符(称为”元字符”)组成的文字模式。模式描述在搜索文本时要匹配的一个或多个字符串。正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
- 普通字符
普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。
- 特殊字符
所谓特殊字符,就是一些有特殊含义的字符,如tes*t中的*,简单的说就是表示任何字符串的意思。如果要查找字符串中的*符号,则需要对*进行转义,即在其前加一个\,如tes\*t匹配tes*t。许多元字符要求在试图匹配它们时特别对待,若要匹配这些特殊字符,必须首先使字符”转义”,即,将反斜杠字符\放在它们前面。
- 限定符
限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。常用有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 等等。
- 定位符
定位符用来描述字符串或单词的边界,^和$分别指字符串的开始与结束,\b描述单词的前或后边界,\B表示非单词边界。
下面表格列出了常用的正则表达式符号并给出了说明:
包含’ \ ’的特殊序列的意义如下表:
特殊表达式序列 | 意义 |
---|---|
\n | 匹配一个换行符,等价于\x0a和\cJ。 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。例如,’er\b’可以匹配”never”中的’er’,但不能匹配”verb”中的’er’。 |
\B | 匹配非单词边界,’er\B’能匹配”verb”中的’er’,但不能匹配”never”中的’er’。 |
\d | 匹配任意十进制数,相当于[0-9]。 |
\D | 匹配任意非数字字符,相当于[^0-9]。 |
\s | 匹配任意空白字符,相当于[ \t\n\r\f\v]。 |
\S | 匹配任意非空白字符,相当于[^ \t\n\r\f\v]。 |
\w | 匹配任意数字和字母,相当于[a-zA-Z0-9_]。 |
\W | 匹配任意非数字和字母的字符,相当于[^a-zA-Z0-9_]。 |
\r | 匹配一个回车符,等价于\x0d和\cM。 |
五、Sed使用案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# 删除/root/passwd文件中的第1到第5行,其余行显示; $ sed '1,5d' /root/passwd # 删除/root/passwd文件中的第3行到最后一行,其余行显示; $ sed '3,$d' /root/passwd # 删除/root/passwd文件中的奇数行,其余行显示; $ sed '1~2d' /root/passwd # 删除/root/passwd文件中所有包含root的行,其他行输出; $ sed '/root/d' /root/passwd # 删除/root/inittab文件中的空白行; $ sed '/^$/d' /root/inittab # 删除/root/fstab文件中以/开头的行,其余行显示(\表示转义); $ sed '/^\/d/' /root/fstab # 显示/root/fstab文件中以/开头的行(sed默认会显示整个文本加/开头的行); $ sed '/^\/p/' /root/fstab # 静默模式只显示以/开头的行; $ sed -n '/^\//p' /root/fstab # 显示/root/passwd文件中所有有root的行并显示; $ sed -n '/root/p' /root/passwd # 在/root/fstab文件的第一行下面添加"#hello world"; $ sed '1 a #hello world' /root/fstab # 在/root/fstab文件中以/开头的行添加两行#hello world,\n表示换行; $ sed '/^\/a #hello world\n#hello world' /root/fstab # 在/root/fstab文件中的2到4行替换成#hello; $ cat -n /root/fstab | sed '2,4 c #hello' # 在/root/fstab文件的第一行和第二行后面添加/etc/issue中的内容; $ sed '1,2r /root/issue' /etc/fstab # 把/root/fstab文件中以/开头的行保存到1.txt中; $ sed '/^\//w /tmp/1.txt' /root/fstab # 把文件中匹配到root的都换成ROOT; $ sed 's/root/ROOT/' /root/fstab # 把/root/fstab文件中所有的/都换成#; $ sed -n 's/\//#/g p' /root/fstab # 把/root/fstab中所有的/都换成#,使用@就不用转义了; $ sed 's@\@#@g' /root/fstab # 把/root/inittab文件中以id开头行中所有的5都换成3; $ sed -n '/^id/s/3/5/ p' /root/inittab # 把/root/inittab文件中前5行中的#号都删除; $ sed -n '1,5 s/#// p' /root/inittab # 把/root/fstab文件中前4行行首添加#号; $ sed '1,4 s/^/#/' /root/fstab # 把/root/fstab文件所有行首添加#号; $ sed 's/^/#/g' /root/fstab # 把/root/fstab文件中前4行行首#号删除; $ sed '1,4 s/^#//' /root/fstab # 把/root/fstab文件中前4行行尾添加#号; $ sed '1,4 s/$/#/' /root/fstab # 把history命令执行结果中开头为空白字符的都删除; $ history | sed 's/^[[:space:]]*//' # sed命令取IP地址; $ ifconfig eth0 | sed -n '/inet addr/p' | sed 's/^.*addr://g' | sed 's/Bcast.*$//g' # 一次可以执行多条命令; $ sed -e '3,$d' -e 's/bash/ksh/' /root/passwd # 直接可以修改/root/initab文件(比较危险甚用); $ sed -i '/^id/s/3/5/' /etc/inittab |
分组案例:Sed只显示匹配的行
Sed分组”\(\)”,就是将()之间的表达式定义为”组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用\1到\9的符号来引用。
1 2 3 4 5 |
$ STR='MAIL FROM(ABCD) BODY' $ echo $STR | sed 's/^.*FROM\((.*)\).*$/\1/g' (ABCD) $ echo $STR | sed 's/^.*FROM(\(.*\)).*$/\1/g' ABCD |
最后来一个好玩的,文件内容如下。
1 2 3 |
Grants for root@localhost GRANT PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION; GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION; |
通过sed处理成这个样式:
1 2 3 |
-- Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION; GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION; |
Sed语法如下:
1 |
$ sed 's/\(GRANT .*\)/\1;/;s/^\(Grants for .*\)/-- \1/;/--/{x;p;x;}' |
分解成三个语句(每个语句以”;”隔离):
1 |
s/\(GRANT .*\)/\1;/ |
s///,把行中有\(GRANT .*\)的匹配出来,然后交由\1;替换,其中前面\(GRANT .*\)使用了()分组,所以后面可以直接使用\1去引用,同时添加一个”;”号并显示。
1 |
s/^\(Grants for .*\)/-- \1/ |
s///,把行中以\(Grants for .*\)开头的行匹配出来,然后交由– \1替换,其中前面\(GRANT .*\)使用了()分组,所以后面可以直接使用\1去引用,同时添加”– “并显示出来。
1 |
/--/{x;p;x;} |
最后一个就好玩了,/–/表示匹配到有–的行就交由后面的{x;p;x;}处理,这个{x;p;x;}是sed的一个命令,其中x表示把模式空间,也就是”/–/”匹配到的行跟保留空间做替换(保留空间可以理解为一个空行);然后p表示把模式空间的内容显示出来,由于做了替换所以模式空间显示的就是一个空行;最后一个x表示把模式空间跟保留空间再做一次替换,然后显示出来,等于又把原来模式空间”/–/”匹配到的行给显示出来;最后的结果就是显示成这个样子了,匹配到的行前面加了一个空行。
1 |
-- Grants for root@localhost |
<参考>