正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。
正则表达式主要分两种:POSIX标准正则表达式和perl正则表达式(PCRE)
注意:
-
现在的编程语言中的正则表达式,大部分都属于Perl正则表达式。
POSIX把正则表达式分为两种:
-
基本的正则表达式(Basic Regular Expression 又叫 Basic RegEx 简称 BREs)
-
扩展的正则表达式(Extended Regular Expression 又叫 Extended RegEx 简称 EREs)
基本和扩展正则的区别:
-
支持的元字符不一样
-
BRE,只有^$.[]*是元字符
-
ERE,^$.[]*+(){}?|都是元字符
-
ERE中新加的字符在BRE里只是普通的字符,需要转义才行
默认使用BRE语法的命令有:grep、sed、vim
默认使用ERE语法的命令有:egrep、awk
1、文本处理工具
-
grep,以行为单位对文本进行过滤
-
cut,对行进行截取
-
sed,对文本进行修改
-
awk,神器
1.1、grep
-
支持:BREs、EREs、PREs 正则表达式
-
grep 指令后不跟任何参数,则表示要使用 ”BREs“
-
grep 指令后跟 ”-E" 参数,则表示要使用 “EREs“
-
grep 指令后跟 “-P" 参数,则表示要使用 “PREs"
作用:
-
文本搜索工具,根据用户指定的“模式”对目标文件逐行进行匹配检查,并打印匹配到的行
-
所谓“模式”,是由正则表达式字符及文本字符所编写的过虑条件
#从/etc/passwd文件中找到带有root的行
grep root /tmp/passwd
#-i,忽略大小写
grep -i ROOT /tmp/passwd
#输出带有root的行的行号
grep -n root /tmp/passwd
#-v 反选
grep -v root /tmp/passwd
#-A 1 表示after,把含有关键字的行的下一行也输出
grep -A 1 -n root /tmp/passwd
#-B 1 表示before,上一行
grep -B 1 -n root /tmp/passwd
#抓取含有root和test的行
#-e 表示扩展的grep,用于当你要抓取的关键字不在同一行时,但这种方式比较麻烦,可以使用egrep
grep -e root /tmp/passwd -e user1 /tmp/passwd
1.2、egrep
-
支持:EREs、PREs 正则表达式
-
不跟任何参数,则表示要使用 “EREs”
-
“-P" 参数,则表示要使用 “PREs"
egrep 'root|user1' /etc/passwd
1.3、cut
-
分割列,同样是以一行为单位,一次处理一行,可以指定这个行当中的分隔符号,然后输出一行当中可以被分成的列
#查看第一字段
cut -d: -f 1 /tmp/passwd
#将分隔符改为空格输出显示
cut -d: -f 1,7 --output-delimiter=' ' /tmp/passwd
#先抓取root行,然后通过cut,输出本行以冒号为分隔符的第一列
grep root /tmp/passwd | cut -d “:” -f 1
#输出1到3列
grep root /tmp/passwd | cut -d “:” -f 1-3
#输出第1 列和第3列
#如果没有指定分隔符,默认以一个空格为分隔符
grep root /tmp/passwd | cut -d “:” -f 1,3
1.4、sed
#打印第1到10行,但是其它行也会输出
#这里的逗号是to的意思
#p 代表打印
sed ‘1,10p’ /tmp/passwd
#-n 表示静默输出,只打印处理过的行
sed -n ‘1,10p’ /tmp/passwd
#只打印包含root关键的行
sed -n '/root/p' /tmp/passwd
#d 删除,删除1到10行
sed ‘1,10d’ /tmp/passwd
#a 增加,在第1行下面,增加一行,内容是2222
#i 插入,在上一行插入
#c 替换,这里要注意:sed ‘1c2’ /tmp/passwd,这里表示把包含1的整行替换成2
sed ‘1a2222’ /tmp/passwd
#全局替换关键字root为hehe
#s/regexp/replacement/,替换语法
#1,$,表示第一行到最后一行,默认只匹配每一行的第一个关键字
#g,表示每行全局匹配
sed ‘1,$s/root/hehe/g’ /tmp/passwd
以上的操作都不会实际去更改文件,如果想直接修改文件,要使用sed -i
1.5、awk
awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。
awk是以文件的一行为处理单位的。awk每接收文件的一行,然后执行相应的命令,来处理文本。
域:以指定分隔符分隔开的字段,一个字段是一个域,也就是我们上面说的列
语法:
awk -F|-f|-v 'BEGIN{ } / / {comand1;comand2} END{ }' file
-
-F 定义列分隔符
-
-f 指定调用脚本
-
-v 定义变量' '引用代码块,awk执行语句必须包含在内
-
BEGIN{ } 初始化代码块,在对每一行进行处理之前,初始化代码,主要是引用全局变量,设置FS分隔符
-
{ } 命令代码块,包含一条或多条命令// 用来定义需要匹配的模式(字符串或者正则表达式),对满足匹配模式的行进行上条代码块的操作
-
END{ } 结尾代码块,在对每一行进行处理之后再执行的代码块,主要是进行最终计算或输出结尾摘要信息
AWK内置变量:
NR |
表示已读的行数 |
FNR |
处理多个文件时,每个文件的行号从1开始计数 |
NF |
表示记录的域的个数 |
FS |
和-F选项一个意思,指定分隔符 |
ARGC |
命令行参数个数 |
ARGV |
命令行参数排列 |
ENVIRON |
支持队列中系统环境变量的使用 |
FILENAME |
awk浏览的文件名 |
OFS |
输出域分隔符 |
ORS |
输出记录分隔符 |
RS |
控制记录分隔符 |
#ip的输出是不规则的,每列中间的空格数不一样,这种情况,对于cut就不适用了,也就出现了awk
ip addr |sed -n '9p' |cut -d " " -f 2
#打印出ip命令输出内容的第9行,以任意多个空格分隔的第2个域
#NR==2 表示第2行
#{}里面的内容为动作
#print $2 表示打印第2个域的内容
#$0 表示当前行的行号,print $0,就是打印整行
ip addr |awk 'NR==9{print $2}'
-
NR<2 #小于第2行的,也就是第一行
-
NR<=2 #小于等于第2行,也就是第一行和第二行
-
NR>2 #大于等于第2行,也就是从第三行开始往后的所有行
-
NR>=2 #大于等于第2行,也就是从第二行开始往后的所有行
-
NR!=2 #不等于,也就是除了第二行的所有行
#NR==NF,表示过滤出行号和域个数一样的行,比如:第7行有7个域,条件就成立,而其它行,都有7个域,但行号不等于7,所以条件不成立。
#$1,表示取前面过滤的行的第一个域
awk -F : ‘NR==NF{print $1}’ /tmp/passwd
#打印出第7行以冒号分隔的域的个数
awk -F : ‘NR==NF{print NF}’ /tmp/passwd
#在每行前面打印行号
awk -F: '{print NR,$0}' /tmp/passwd
#如果同时处理多个文件,默认行号只会往后加
#FNR,表示处理第二个文件时,行号从1开始
awk -F: '{print FNR,$0}' /tmp/passwd /tmp/shadow
截取关键字
#打印出第一个域里包含有root关键字的所有行
awk -F : ‘$1~”root”{print }’ /tmp/passwd
#打印出第一个域是root的行
awk -F : ‘$1==”root”{print }’ /tmp/passwd
#打印包含root的行
awk -F : ‘/root/’ /tmp/passwd
#在执行过滤动作之前,先执行完BEGIN的内容,如果没有就不执行
#在执行过滤动作之后,再执行END的内容,如果没有就不执行
awk -F : 'BEGIN {print "name"} {print $1} \
END {print "hehe"}' /etc/passwd
#打印变量
awk -F ':' '{print "filename:" FILENAME ",linenumber:" NR ",\
columns:" NF ",linecontent:"$0}' /tmp/passwd
2、元字符
所谓元字符就是指那些在正则表达式中具有特殊意义的专用字符
字符 |
意义 |
^ |
匹配开头 |
$ |
匹配结尾 |
() |
标记一个表达式的开始和结束位置。分组 |
* |
匹配前面的子表达式0次或多次 |
+ |
匹配前面的子表达式1次或多次 |
. |
匹配除换行符\n之外的任何单个字符 |
? |
匹配前面的子表达式0次或1次 |
| |
匹配两项中的一个 |
-
BRE:只有^$.[]* 是元字符
-
ERE:^$.[]*+(){}?|都是元字符
-
ERE中新加的字符在BRE里只是普通的字符,需要转义才能使用。
扩展正则表达式与基本正则表达式的区别:
-
基本正则表达式中,| ? + () {}需要转义
-
扩展正则表达式中,| ? + () {}不需要转义
注意:
-
使用单引号把正则表达式括起来,防止被shell当作其元字符解析
-
要和通配符区分开来,通配符是shell的特殊字符,只涉及* ? [] {}这四种符号。
2.1、字符匹配
-
点(.)
表示有且只有一个任意字符
#r和t之间有两个任意的字符
grep 'r..t' /root/passwd
-
[]
一个中括号只表示一个字符,中括号里面的内容表示其中一个字符
#查找root、aoot、boot
grep [rab]oot /root/passwd
#如果^在中括号里面表示取反,也就是说查找不包含root、aoot、boot的行
#如果^在中括号外,就表示以什么开头的意思
grep [^rab]oot /root/passwd
-
[^]
取反
2.2、次数匹配
-
?
表示?号前面的字符只有一个或零个
#过滤rt,或rot
egrep 'ro\?t' /mnt/passwd
-
*
表示*号前面的字符有任意个,包括0个。
#r和t之间,可以有任意多个o,包括没有
#如:rt,rot,root
grep ro*t /root/passwd
-
+
表示+号前面的字符有1个或以上。
#过虑出r和t之间,最少有一个o的行
grep 'ro\+t' /etc/passwd
-
{}
重复前面字符次数
#把大括号前面字符重复2次,也就是roo
#注意:大括号在bash中,表示展开,要加脱义符
grep ‘ro\{2\}’ /root/passwd
#重复1到3次,也就是包含ro、roo和rooo的行都打印出来
grep ‘ro\{1,3\}’ /root/passwd
#最多匹配3次
grep 'ro\{0,3\}' /root/passwd
#至少匹配3次
grep 'ro\{3,\}' /root/passwd
2.3、位置锚定
-
^
以什么开头
#打印出以root开头的行
grep ^root /root/passwd
-
$
以什么结尾
#打印出以bash结尾的行
grep "bash$" /root/passwd
-
\< 或 \b
定位一个单词的首部
#查找包含以r开头的root单词的行
grep '\<root' /etc/passwd
-
\> 或 \b
定位一个单词的尾部
#查找包含以t结尾的root单词的行
grep 'root\>' /etc/passwd
#匹配以m开头,e结尾的单词
grep '\bm[a-z]*e\b' /etc/passwd
#同上
grep '\<m[a-z]*e\>' /etc/passwd
#同上
#\w,匹配字母或数字或下划线或汉字
grep '\bm\w*e\b' /etc/passwd
2.4、分组
-
() \1 \2
以()括起来的正则表达式,在后面使用的时候可以用\1、\2等变量来访问()中匹配到的内容。
#过虑最少有一组ro的行
grep '\(ro\)\+' /root/passwd
#\1,表示前面第一个分组匹配的字符
#注意,分组括号中匹配到的内容会被正则表达式引擎记录于内部的变量中,这些变量命名方式为:\1,\2,\3等
#如:\(ab\+\(xy\)*\),\1变量对应的是ab\+\(xy\)*,\2变量对应的是:xy
#也就是说,.*前后的字符要一样
grep '\(ro\).*\1' /root/passwd
2.5、分枝
-
a|b
#a或b
2.6、扩展
-
\t
制表符
#-e,允许转义符,也就是允许使用控制符
echo -e "how \tare you"
-
\n
换行符
echo -e "how \nare you"
3、结合使用
3.1、grep
注意,grep后面跟的元字符尽量加上双引号或单引号
#查找空行
#注意,^$,只表示空行,如果这行中有一个空格,就不能算了,所以建议使用第一条
grep "^[[:space:]]*$" /root/passwd
grep "^$" /root/passwd
#b和t中间,可以有任意多个任意字符
grep "b.*t" /etc/passwd
#查找t前面有3个字母的行
grep '[[:alpha:]]\{3\}t' /etc/passwd
#查找以t前面有1到3个字母为首的单词的行
grep '\<[[:alpha:]]\{1,3\}t' /etc/passwd
#查找以2到3位数字为一个单词的行
grep "\<[0-9]\{2,3\}\>" /etc/passwd
#过滤出netstat -tan命令中,以LISTEN结尾的行
#发现,LISTEN后面其实还有多个空白字符
netstat -tan |grep "LISTEN[[:space:]]*$"
#过滤root和ftp两个关键字
#-e,使用扩展正则表达式
grep -e 'root\|ftp' /mnt/passwd
#过滤出IP地址
#IP地址,数字必须在255以内
grep '\(\(2[0-4][0-9]\|25[0-5]\|[01]\?[0-9][0-9]\?\)\.\)\{3\}\(2[0-4][0-9]\|25[0-5]\|[01]\?[0-9][0-9]\?\)' /mnt/f1
3.2、贪婪匹配和懒惰匹配
默认,正则表达式和扩展正则表达式,是贪婪的,最长匹配。
PERL正则表达式,可在量词后面直接加上一个问号(?),实现懒惰匹配,最短匹配
注意:
-
?实现懒惰匹配,只适合PCRE,不适合POSIX标准正则表达式
#最短匹配
#-o,只输出匹配到的字符
#-P,使用perl正则表达式
#注意,使用perl正则,不需要转义
grep -oP 'r.*?t' /mnt/passwd
3.3、egrep
扩展表达式,()不用转义
#查找包含独立单词root或者ssh开头的行
#发现sshd开头的行并没有找到,那是因为,表达式要求是以ssh为独立单词的行,并不是sshd。
egrep '^(root|ssh)\>' /etc/passwd
#|,表示或者
egrep ‘root|ftp’ /tmp/passwd
#查找root或者boot关键字的行
egrep '(r|b)oot' /tmp/passwd
#过滤IP
egrep '(([01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\.){3}([01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])' /mnt/f1
3.4、sed
#删除空行
sed -i ‘/^$/d’ /root/passwd
3.5、awk
#以冒号为分隔符,只显示以root开头的行的第一列
awk -F “:” ‘/^root/{print $1}’ /root/passwd
#过虑出任意字母开头,中间包含t:的行
awk -F: '/^[a-z]*t:.*'/ {print $0}' /etc/passwd