两个概念,通配符与正则表达式,这两个规范的实现都是由软件所提供的,如通配符:shell和find,而它们的实现之间还有区别如:shell的[ ]里面是不区分大小写的。而find是区分大小写的。
而正则表达式这一种更为强大的搜索规范所使用的软件如:vi,sed,grep,awk。
Linux上文本处理三剑客:
grep, egrep, fgrep:文本过滤工具(模式:pattern)工具;
grep:基本正则表达式,-E (egrep),-F(fgrep)
egrep:扩展正则表达式, -G(grep),-F(fgrep)
fgrep:不支持正则表达式,性能更好。
sed:stream editor, 流编辑器;文本编辑工具;
awk:Linux上的实现为gawk,文本报告生成器(格式化文本);
使用:http://fanqie.blog.51cto.com/9382669/1650244
正则表达式:Regual Expression, REGEXP
由一类特殊字符(元字符)及文本字符所编写的模式,这些元字符不表示其字面意义,而是用于表示控制或通配的功能.
分两类:
基本正则表达式:BRE
扩展正则表达式:ERE
目录:
一、grep
1、正则表达式
2、扩展正则表达式
二、sed
一、grep
grep: Global search REgular expression and Print out the line.
作用:文本搜索工具,根据用户指定的“模式(过滤条件)”对目标文本逐行进行匹配检查;打印匹配到的行;
模式:由正则表达式的元字符及文本字符所编写出的过滤条件;
正则表达式引擎;
grep [OPTIONS] PATTERN [FILE...]
grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]
OPTIONS:
--color=auto:对匹配到的文本着色后高亮显示;
-i:ignorecase,忽略字符的大小写;
-o:仅显示匹配到的字符串本身;
-v, --invert-match:显示不能被模式匹配到的行;
-E:支持使用扩展的正则表达式元字符;
-q, --quiet, --silent:静默模式,即不输出任何信息;
-A #:after, 后#行
-B #:before,前#行
-C #:context,前后各#行
1、基本正则表达式
字符匹配:
. | 点号匹配任意单个字符。 |
[] | 匹配里面的任意单个字符,如:[acdb] a,c,d,b,这四个单字符都会匹配 |
[^] | 不匹配里面的任意单个字符。跟上面完全相反,这四个单字符都不会匹配。 |
上面的[ ]之间也可以用下面这些字符组。它们代表一部分字符的组合。
[:upper:] 大写字母
[:lower:] 小写字母
[:alpha:] 所有字母
[:digit:] 所有数字
[:alnum:] 所有字母和数字
[:space:] 空格
[:punct:] 标点符号
如:查找大写字母[[:upper:]]。里面的[:upper:]是一个整体,与外面的[ ]没有关系,所以不能省略。
次数匹配:用在要指定其出现的次数的字符的后面,用于限制其前面字符出现的次数;默认工作于贪婪模式;
* | 任意次前面的字符。包括0次。如:s* 没有s的也会匹配。 |
.* | 只是一种应用方法,代表任意个任意字符。 |
\? | 前面字符的0次或1次。 |
\{m\} | 前面字符的m次。手动指定前面字符的次数。 |
\{m,n\} | 前面的字符的m-n次。如:\{2,3\} 2-8次前面的字符。 |
\{m,\} | 前面的字符至少m次。没有上限,只有下限。 |
\{0,n\} | 前面的字符至多只有n次。也就是从0到n个。 |
\+ | 前面的字符等于大于1次,与*相比,没有0次了。 |
位置锚定:用于指定字符出现的位置。
^ | 指定行首 |
$ | 指定行尾 |
^$ | 这个只是一种应用方法, 代表空白行。 行首跟着行尾 空白行。 |
\<char | 查找以字符开头的单词。 用 \b 也行。 \bchar char是字符的意思。 |
char\> | 查找以字符结尾的单词。 用\b 也行。 char\b |
组:
\(\) | 用于把单个字符组合成多个字符, 以组的方式再来次数匹配。 而且可以被引用。 组的功能:1、把多个字符组合成整体来做次数匹配。2、可以后向引用 |
\# | 后向引用。#为数字,可以引用第#个组的匹配结果,也就是第#个小括号。注意引用的是组所匹配到的结果,而不是把组的功能给复制过来。 |
\| | 或。如:"^abc|^def", 开头是abc,或者开头是def的。也可以"^\(abc\|def\)" |
分组括号中的模式匹配 到的内容会被正则表达式引擎自动记录于内部的变量中,这些变量为:
\1:模式从左侧起,第一个左括号以及与之匹配的右括号之间的模式所匹配到的字符;
\2:模式从左侧起,第二个左括号以及与之匹配的右括号之间的模式所匹配到的字符;
...
关于后向引用。老师上课的时候,所给出的很有意思的题。
找出前后一样的love或者like。就是前是love后面就要是love.
He loves his lover.
He likes his lover.
She likes her liker.
She loves her liker.
~]# grep "\(l..e\).*\1" lovers.txt
例、查找下以 f 开头,中间两个任意字符,以 m 结尾的都有哪些。
[root@star-linux ~]# grep "f..m" text.tx
But from behind the walls of doubt
[root@star-linux ~]#
例、 第一个字符要么是g 要么是m,中间是两个一样的,结尾一个d,怎么查 [ ] \{m\}
[root@star-linux ~]# grep "[gm].\{2\}d" text.tx
good morning everyone
I had a dream, I had an awesome dream
As we go down life's lonesome highway
[root@star-linux ~]#
结果出乎意料, 这是因为 . 是任意字符, 而\{2\} 是代表2个前面的字符,也就是两个任意字符了(包括空格),这个题做的有问题。在要求前后一样的情况下,就要用到后向引用了。
例、用组和后向引用。\(\) \# 后向引用
[root@star-linux ~]# grep "[gm]\(.\)\1d" text.tx
good morning everyone
[root@star-linux ~]#
我们要的是这个结果。 上面的意思就是小括号里面的 . 所匹配到的是什么结果,后面\1也就是什么结果, 在个例子中,小括号是 o , 那么 \1 也就是 o.
例、查找下行开头是A 或者 As 。 ^ \?
[root@star-linux ~]# grep "^As\?" text.tx
And what they played
A voice was crying out
As we go down life's lonesome highway
[root@star-linux ~]#
例、再查找个单词试试,way , 找这个单词,那我们就来试试。
[root@star-linux ~]# grep "way" text.tx
Say it for always
That's the way it should be
Say it for always
That's the way it should be
Say it for always
.........
[root@star-linux ~]#
What? 搞什么仙人板板,把有关way的字符串都给拉出来了。 那么 \< \>
[root@star-linux ~]# grep "\<way\>" text.tx
That's the way it should be
That's the way it should be
When you feel you've lost your way
That's the way it should be
That's the way it should be
[root@star-linux ~]#
\< \> 指定单词用的,这两个符号的两边只允许出现空格或者标点符号。如果是字母则不匹配。
例、查找以组为单位的重复字符。组的次数匹配, \(\) \{m,\}
[root@star-linux ~]# grep "\(way\)\{2,\}" text.tx
Say it for alway waywayway ways
[root@star-linux ~]#
这个意思就是,way这个整体,重复两次到无限多次。
例、再看看行尾是怎么回事,查找行尾是 . 的,有意思吧,注意 . 可是元字符,意思就不用说了吧, 那怎么让它变成普通字符, 转义字符\ $
[root@star-linux ~]# grep "\.$" text.tx
it's fine day today.
There are many birds flying in the sky.
[root@star-linux ~]#
\ 的功能就是转义。把有意义的字符,转成普通的。把普通字符转成有意义的,当然如果是彻彻底底的普通字符,再转也没用。
而在正则表达式里的加\的元字符, 是因为规范的问题所导致的,在grep所用的正则表达式里,那些需要加\的元字符,都是普通字符,需要加上\来转义,如果不加的话,它就是普通字符。
而在egrep 所用的扩展的正则表达式中, 除了\< \>和后向引用还要用转义外, 其它的都不用转义了,
这又是另外的一种规范, 如果非要加转义符, 反而又变成普通字符了。
2、扩展正则表达式
主要区别就是次数匹配的元字符不用加\了.
egrep:
支持扩展的正则表达式实现类似于grep文本过滤功能;grep -E
egrep [OPTIONS] PATTERN [FILE...]
选项:
-i, -o, -v, -q, -A, -B, -C
-G:支持基本正则表达式
元字符:
. | 任意单个字符 |
[] | 范围内的任意单个字符 |
[^] | 范围外的任意单个字符 |
次数匹配:
* | 匹配任意次前面的字符 |
? | 匹配0或1次前面的字符 |
+ | 匹配至少1次前面的字符 |
{m} | 匹配其前面的字符m次 |
{m,n} | 匹配其前面的字符m-n次 |
{m,} | 匹配其前面的字符至少m次 |
{0,n} | 至多n次 |
位置锚定:
^ | 行首 |
$ | 行尾 |
\<或\b | 词首 |
\>或\b | 词尾 |
分组:
( ) | 分组。 组的功能 1、把多个字符组合成整体来做次数匹配。2、可以后向引用 |
| | 或者 a|b:a或者b;C|cat:C或cat |
(|) | 组中的或 (c|C)at:cat或Cat |
跟基本正则表达式,除了不用加\以外,没有什么其它的区别。这里来些例子做些参考
取路径基名:
匹配IP地址:
匹配1.0.0.1--223.255.255.254 IP地址的写法
[root@localhost ~]# ifconfig | egrep '(\<(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\>\.){3}\<(1?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\>' inet 172.16.249.185 netmask 255.255.0.0 broadcast 172.16.255.255 inet 192.168.189.190 netmask 255.255.255.0 broadcast 192.168.189.255 inet 127.0.0.1 netmask 255.0.0.0
匹配IP地址一般也没必要一定要匹配1-254之间。也可以直接匹配0-999的数字。
[root@localhost ~]# ifconfig | grep -E --color=auto "\<([0-9]{1,3}\.){3}[0-9]{1,3}\>" inet 172.16.249.185 netmask 255.255.0.0 broadcast 172.16.255.255 inet 192.168.189.190 netmask 255.255.255.0 broadcast 192.168.189.255 inet 127.0.0.1 netmask 255.0.0.0
二、sed。
sed [OPTION]... 'script' [input-file] ...
script:地址定界编辑命令
常用选项:
-n:不输出模式空间中的内容至屏幕;真实的效果就是不显示没有区配到的。如果编辑命令是p,那正好只显示区配到的,如果是d,那就会什么也不显示了,因为区配到的给删除了。
-e script, --expression=script:多点编辑;
-f /PATH/TO/SED_SCRIPT_FILE 从文件读取命令,每行一个编辑命令;
-r, --regexp-extended:扩展的正则表达式
-i[SUFFIX], --in-place[=SUFFIX]:直接编辑原文件 ;
地址定界:
(1) 空地址:对全文进行处理;
(2) 单地址:
#:指定行;
/pattern/:被此模式所匹配到的每一行;
(3) 地址范围
#,#:
#,+#:
#,/pat1/
/pat1/,/pat2/
$:最后一行;
(4) 步进:~
1~2:所有奇数行
2~2:所有偶数行
从第几行开始,步长为几。如0~10,5~3。
编辑命令:
d:删除;
p:显示模式空间中的内容;
a \text:在行后面追加文本“text”,支持使用\n实现多行追加;
i \text:在行前面插入文本“text”,支持使用\n实现多行插入;
以上如果text之前没有要显示的空字符,可以不用\.
c \text:把匹配到的行替换为此处指定的文本“text”;
w /PATH/TO/SOMEFILE:保存模式空间匹配到的行至指定的文件中;
r /PATH/FROM/SOMEFILE:读取指定文件的内容至当前文件被模式匹配到的行后面;文件合并;
=:为模式匹配到的行打印行号;
!:条件取反;格式:!编辑命令;
s///:查找替换,其分隔符可自行指定,常用的有s@@@, s###等;
替换标记:
g:全局替换;
w /PATH/TO/SOMEFILE:将替换成功的结果保存至指定文件中;
p:显示替换成功的行;
sed的工作方式:
每次在磁盘上读取一个块,要看读取文件的范围了。然后在内核所缓存的文件数据中按换行符一次读取一行数据,放到sed自己的内存空间(模式空间)来等待处理。
但处理的时候却还有点不一样,会复制一份数据直接stdout(默认屏幕),一份到处理空间处理。
虽然画的stdou与处理空间一点关系也没有,但还是有点关联的。在用d编译命令的时候,不会直接stdout。
地址定界:
例:输出passwd文件中的2-5行.
[root@localhost ~]# sed -n '2,5p' /etc/passwd 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 [root@localhost ~]#
一般情况下,每行数据都会直接stdout出来。-n就是不要stdout。所以上面显示的只有处理完成的数据。
上面的-n就是常用选项,而2,5的部分就是地址定界,p就是编辑命令。
地址定界也可以用 /pattern/方式,如:
例:输出root所在的数据。
[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 -n '/^root\>/p' /etc/passwd root:x:0:0:root:/root:/bin/bash [root@localhost ~]#
上面第一个例子因为/pattern/写的不够严谨,所以匹配到了两行。
例:显示sshd用户到最后一行。
[root@localhost ~]# sed -n '/^sshd\>/,$p' /etc/passwd sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin tcpdump:x:72:72::/:/sbin/nologin mageedu:x:1000:1000:MageEdu:/home/mageedu:/bin/bash sst:x:1001:1001::/home/sst:/bin/bash [root@localhost ~]#
显示root和以下3行。
[root@localhost ~]# sed -n '/^root\>/,+3p' /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 [root@localhost ~]#
步进,从第几行开始,每次跨几行。如:
从20行开始,以3为跨度。
[root@localhost ~]# sed -n '20~3p' /etc/passwd rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin chrony:x:996:994::/var/lib/chrony:/sbin/nologin ntp:x:38:38::/etc/ntp:/sbin/nologin
编辑命令中有个=号,是用来显示所匹配到的数据的行号。
[root@localhost ~]# sed -n '20~3=' /etc/passwd 20 23 26
编辑命令:
上面的p和=号都有了,下面再来几个。(a,i,c)
[root@localhost ~]# sed '/^root\>/a drink tea' /etc/passwd root:x:0:0:root:/root:/bin/bash drink tea bin:x:1:1:bin:/bin:/sbin/nologin
在root所在行的下面附加一行。a就是这个功能。
添加多行。单引号是绝对引用所以回车也可以被sed识别,用\n代表换行也可以。如果不定界,就是空地址,会在所有行的下面加上这一堆数据。
[root@localhost ~]# sed '2a drink tea\ > drink coffee\ > drink apple juicd' /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin drink tea drink coffee drink apple juicd
而i就是在root行的下面插入一行。
[root@localhost ~]# sed '/^root\>/i drink tea' /etc/passwd drink tea root:x:0:0:root:/root:/bin/bash
而c就是直接修改root行。
[root@localhost ~]# sed '/^root\>/c drink tea' /etc/passwd drink tea bin:x:1:1:bin:/bin:/sbin/nologin
编辑命令:d
sed '2d' 删除第二行
sed '2,5d' 删除2到5行
sed '2,$d' 删除2到尾行
sed '/star/d' 删除有star的行
编辑命令:s
sed '行号s/老的字符串/新的字符串/g'
以行单位查找匹配的字符串,然后替换。
sed 's/:/?/g' 把数据中所有行的:变成?。
sed '2,5s/:/?/g' 把2-5行的:变成?。
sed '/star/s/#/#star/g' 把有star的行中的#变成#star。
删除/etc/fstab文件中所有以#开头的行的行首的#号及#后面的所有空白字符;并且删除空行。
[root@localhost ~]# sed -e 's/^#[[:space:]]*//g' -e '/^$/d' /etc/fstab /etc/fstab Created by anaconda on Sun Dec 6 11:02:42 2015 Accessible filesystems, by reference, are maintained under '/dev/disk' See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info UUID=1f89bbf3-c25c-4b4b-aac8-284c19077770 / xfs defaults 0 0 UUID=1591ce0b-5726-4e03-8cbe-0a2105169930 /boot xfs defaults 0 0 UUID=0a527ef7-eb80-4155-9cd5-ea12fba99515 /usr xfs defaults 0 0 UUID=7f892a1b-055a-498f-a812-64086132157a swap swap defaults 0 0 [root@localhost ~]#
输出一个绝对路径给sed命令,取出其目录,其行为类似于dirname;
[root@localhost ~]# echo "/etc/sysconfig/network-scripts/ifcfg-eno16777736/" | sed -r 's/[^\/]+\/?$//' /etc/sysconfig/network-scripts/ [root@localhost ~]#
把/etc/fstab文件中的所有UUID的行,最后面的0变成1.
[root@localhost ~]# sed -r '/^UUID\>/s/0([[:space:]]*)$/1\1/g' /etc/fstab # # /etc/fstab # Created by anaconda on Sun Dec 6 11:02:42 2015 # # Accessible filesystems, by reference, are maintained under '/dev/disk' # See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info # UUID=1f89bbf3-c25c-4b4b-aac8-284c19077770 / xfs defaults 0 1 UUID=1591ce0b-5726-4e03-8cbe-0a2105169930 /boot xfs defaults 0 1 UUID=0a527ef7-eb80-4155-9cd5-ea12fba99515 /usr xfs defaults 0 1 UUID=7f892a1b-055a-498f-a812-64086132157a swap swap defaults 0 1
取出/boot/grub/grub.conf文件中的内核文件名称。
[root@localhost ~]# sed -n 's/^[[:space:]]\+kernel \/\(vmlinuz[^[:space:]]\+\).*$/\1/p' /boot/grub/grub.conf vmlinuz-2.6.32-573.el6.x86_64 [root@localhost ~]#
谢谢浏览,有什么不对的地方,还请指出。谢谢。