正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。

    正则表达式主要分两种: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