不少编程高手说计算机领域称得上伟大的发明技术不多,但正则表达式绝对算一个。我姑且相信一回,来准备精通一下。
1.正则表达式介绍
1.1正则表达式引擎
正则表达式相当于一个我们要找的文本内容抽象集合模型,就是说我先建一个正则表达式集合模型,凡是符合这个集合模型的,都可以匹配查找出来。正则表达式的英文翻译是:regular expression,regular有有规律的、规则的意思。翻译成中文,正确规则的表达式,不知道哪个家伙翻译成正则表达式(缩减的贱萌无极限啊。。。。。)
正则表达式是通过正则表达式引擎来驱动的,目前linux常用的正则表达式引擎有两种:
1.POSIX基本正则表达式(BRE)引擎 Basic Regular Expression Engine
2.POSIX扩展正则表达式(ERE)引擎 Extended Regular Expression Engine
很多使用的程序如sed并不完全实现BRE规范,只是其中的一个子集,这主要是为了速度上的考虑
1.2元字符的含义
元字符是指作为正则表达式核心的有语法含义的一些字符,这些字符是表达式的骨架,我们要找的字符串可以放在这个骨架中,构成一个完整的语法来查找文本中的字符串。正则表达式规范认可的元字符有11个,它们是:(这里的冒号不是) .+*^ ?$ ()[]{} \| 中间用空白隔开是将符号分类,这样好记点。
介绍基本的元字符:以hola作为例子
^(数字键6上方的符号):匹配字符串位于任意一个文本行的行首位置的字符串,比如abc hola cde,如果^hola进行匹配将匹配不出来,因为hola不位于行首
$:美刀符号,这个符号找出正则字符串位于任意一行行尾
*:通配符,这个符号能通配任意类型任意个数字符串,比如hola*能匹配出所有以hola开头的字符串,注意不能用只有一个单字符*匹配出全文,*必须与其他字符联合进行匹配。例如grep * portal.log 这个正则命令匹配不出东西,需要*与其他字符一起用,例如grep hola.* portal.log
[]:范围符,只能匹配一个字符,范围符号有两种用法:
一种是将正则字符串某个位置的字符可能值列出来,[某个位置的字符可能值,如abc]即[abc]这个符号的作用是正则字符串的某个字符可能是范围符号里面的任何一个,那么执行正则命令后,得到的字符集里面的对应位置的字符会列出包含范围符所列的字符。比如说我不确定separate的正确写法是什么,可能是seperate,这时候可以用sep[ae]rate进行匹配。
一种是正则字符串某个位置的字符可能是序列型或者连续型字符,这时候与符号-联合起来使用比如sep[a-z]rate,就是第四个字符可能是a-z的任何一个字符,如果我们采用第一种方式需要将26个字符全部列出来,显然这是很麻烦的sep[abcdefghijklmnopqrstuvwxyz]rate(方括号里面的字符顺序随意,只要是字符列表就可以了),显然我们可以采用后一种。另外也可以运行多种类型的字符例如
[a-z0-9A-Z_?.]能够匹配a-z,0-9和A-Z的所有字符还有下划线问号点号,注意a-z0-9A-Z之间没有逗号,此时的-表示范围,是一个元字符,而其他的_?.都表示普通字符。
范围符号[]可以用于解析可能拼错的单词,比如搜索引擎里面进行单词搜索时候,如果输入错误术语,搜索引擎会提示,你可能想输入的是纠正后的什么什么什么
\:转义符号,用来屏蔽元字符的转义,而把元字符当作一个普通字符进行匹配。
?或. :进行任意类型的单个字符匹配,这个与*类似,只不过只能匹配一个。
限定匹配字符次数(以A表示某个字符)
字符A\{n\} 匹配字符A出现n次。pattern表示
字符A\{n,\} 匹配字符A出现最少n次。
字符A\{n,m} 匹配字符A出现n到m次之间,n , m为0 - 2 5 5中任意整数。
排除某个字符组:
^1-6:这个表达式的含义是搜索满足没有1到6共6个数字的那些字符串
2. sed 和gawk实用程序的使用
sed 即stream editor 流式编辑器 流编辑器可以根据提供的规则进行文本编辑,流编辑器每次只读一行,进行规则匹配修改后输出到STDOUT(标准输出,即显示器,可以使用重定向进行变向输出),一行处理完毕后,再处理下一行。重复进行这个过程,直到所有文本处理完毕。
2.1 sed编辑器使用
sed options script file
options有如下三个参数:
-e script 将script中的指定的命令添加到处理输入时执行的命令中
-f file 将file中的指定命令添加到处理输入时执行的命令中
-n 不需要为每个命令产生输出,但需要等待打印结果
sed 支持STDIN输入流,这个功能允许其他命令产生的输出,用管道符号|输入给sed命令进行处理
参数:s 这个参数用于替换
语法:
sed 's/string1/string2/'
这个命令的功能是用string2替换string1
注意:
1.参数s前面没有符号-
2.两个字符串用3个
3.这个命令前面需要有输入流,然后会支持标准输出流和重定向输出,默认的输出是STDOUT即显示器输出。
4.sed并不修改原文件,只是将修改结果发送到STDOUT
两种用法:
1.输出流|sed 's/string1/string2/'
2.sed 's/string1/string2/' 文件名
替换的扩充版:
sed 's/string1/string2/flags'
flags:表示怎样进行替换,有如下几个参数:
a:数字 ,如果是n表示替换第n个数据字段
b:g 用string1替换所有string1
c:p 打印原始行,通常与-n参数一起使用
d:w newfile 将结果写入新文件newfile中,注意:写入新文件的只是匹配模式的那些行,没有匹配修改的不保存
hadoop@sdp15:~/RE> echo "This is a book;This is a desk" | sed 's/This/That/2'
This is a book;That is a desk
hadoop@sdp15:~/RE> echo "This is a book;This is a desk" | sed 's/This/That/g'
That is a book;That is a desk
hadoop@sdp15:~/RE> echo "This is a book;This is a desk" | sed -n 's/This/That/p'
That is a book;This is a desk
hadoop@sdp15:~/RE> echo "This is a book;This is a desk" | sed 's/This/That/p'>fg.txt
hadoop@sdp15:~/RE> echo "This is a book;This is a desk" | sed 's/This/That/w fg.txt'
That is a book;This is a desk
-e参数:支持多个命令
sed -e '模式1;模式2;。。。模式n' 文件
例子:
sed -e 's/string1/string2/;s/string3/string4/' file2
注意:模式之间用分号隔开,模式结尾和分号之间不能有空格
如果模式没有匹配上,则仍然按照原来的输出。
支持多行输入
sed -e '
模式1
模式2
模式n 文件名
-f参数:支持命令脚本
如果需要模式很多,比如进行多个单词替换,可以讲这些模式单独写在一个脚本文件里面,需要用参数-f
vi script1
模式1
模式2
。。。。
模式n
sed -f script1 file2
scrip1是处理目标文件file2的模式脚本
-n 参数禁止sed输出,-p是输出修改的,-n -p输出匹配后的。。。。不知道什么逻辑
可以对比下看看:
hadoop@sdp15:~/RE> vi data.txt
this is a book
this is a desk
hello
hadoop@sdp15:~/RE> sed '/is/p' data.txt
this is a book
this is a book
this is a desk
this is a desk
hello
hadoop@sdp15:~/RE> sed -n '/is/p' data.txt
this is a book
this is a desk
例子:
vi script1
s/hello/hola/
s/but/however/
s/nice/gut/
sed -f script1 file2
2.2 gawk使用
gawk是比sed更高级的流式编辑器,它同时也是一种编程语言,因而可以定义变量,进行更复杂的处理
gawk程序脚本使用大括号{},包含命令
语法:输入流|gawk 参数块 命令块或脚本块 目标处理文件
备注:是命令块或脚本块决定于参数,默认用命令块,用参数-f表示使用脚本,这个与sed一致,把多个命令块放在文件里面就是脚本,命令块包括大括号,不包括单引号
例如:
gawk '{print "Hello World"}'
这个命令需要数据,如果没有重定向数据输入,则需要等待STDIN数据输入,执行上述命令后,会等待用户输入,然后固定输出Hello World,对于用户输入的任何内容,输出的都是Hello World
1.使用数据字段变量
gawk可以进行文本处理,可以将字段定义成变量,然后进行流式处理
$0:表示正行文本
$1:表示文本行的第一个数据字段,即第一列
$n:表示文本行的第n个数据字段,即第n列
数据字段是依据字段分隔符进行确定的,默认的字段分隔符是空白符(包括空格,制表符)
可以指定数据字段分隔符,需要用参数 -F分隔符 进行明确
gawk -F:'{print $0}' file2
这里指定了分隔符冒号
注意:gawk 参数块 命令块 文件 这几个模块之间需要用空格隔开,如果连在一起了系统会识别不出来或者报别的难以料到的问题。
2.在脚本中使用多个命令
所有的命令都用一个共同的大括号包起来,命令之间用分号隔开
echo "Hello World" | gawk '{$2="Hola";print $0}'
3.使用参数-f,将命令块放在脚本中
vi script2
{print $5"'s userid is " $1}
gawk -F: -f script2 file2
gawk 引用一个字符串时候,不需要用美元符号
4.在处理数据之前,运行脚本
使用BEGIN关键字
gawk 'BEGIN 命令' file2
hadoop@sdp15:~/RE> gawk 'BEGIN {print "HELLO WORLD"}{print $0} END {print "Buenas Noches"}'
HELLO WORLD
Buenas Noches
5.使用END处理完数据后继续执行脚本
执行带BEGIN命令后,按ctl+D则执行END后的脚本
hadoop@sdp15:~/RE> gawk 'BEGIN {print "HELLO WORLD"}{print $0} END {print "Buenas Noches"}'
HELLO WORLD
Buenas Noches
退出gawk需要生成EOF(END OF FILE)结束运行,只需按ctl+D就可以了
6.将多个命令脚本化
注意:命令的大括号紧跟在BEGIN后面,不然系统会识别不了,报gawk: script3:2: BEGIN blocks must have an action part
BEGIN{
print "The latest list of all users and shells"
print "Userid Shell"
print "------ -----"
FS=":"
}
{
print $1 " " $7
}
END{
print "Hello World"
}
对比:gawk和sed
两种命令的模块都是:gawk/sed 参数快 单引号包的命令块 文件
7.字符串分隔符
我们用gawk和sed进行文本处理时候,都是用/作为字符串分隔符的,这个分隔符遇到路径时候比如/bin/bash,如果遇到路径需要替换成/bin/csh(比如进行程序移植时候,有些是不支持bash的),这时候用/进行就会很混乱(\表示转义)
sed 's/\/bin\/bash/\/bin\/csh'
这时候可以换一种支持部分元字符作为替换符号如?,(,!,可以用感叹号,这个比较明显
sed 's!/bin/bash!/bin/csh!'
正则表达式:定义BRE模式
gawk '/book/{print $0}'
sed -n '/book/p'
正则表达式规则
规则1:正则表达式区分大小写
规则2:如果没有元字符^和$限定字符串出现的位置,匹配将不限定位置
规则3:匹配字符串时候,如果空格也是字符串的一部分,也正常匹配
例如sed -n '/ /p' file可以匹配出有空格的行
正反斜杠/\都属于特殊字符,如果进行匹配都需要用转义字符\进行转义处理,用\\和\/进行匹配
sed进行正则表达式匹配时候,匹配规则放在//中如:echo "this is a good book"|sed -n '/^book/p',^又称为脱字符,^如果放在其他位置,就属于普通字符,而不表达匹配行首字符串的意思
^string1$:两个字符联合定位,定位的是某一个完整的行
^$:这个匹配没有任何内容的行,可以利用这个特点来删除空行
sed -n '/^$/d' file
上述:d表示删除
点字符:点字符用于匹配任何一个除了换行符以外的字符,注意:点字符必须匹配一个字符而且只能匹配一个字符,如果圆点位置不存在字符,那么无法匹配。
echo "at ten ,we come back"|sed -n '/.at/p',这一句将无法匹配出行首的at,除了通配符*?允许配成0个外,其他的元字符是必须匹配至少可能一个的
模式是匹配结果的抽象,也是匹配结果任何一个字符串的一个子部分,记住这一点
除了通配符*可以匹配任意多个字符外,其他的符号是不能匹配多个字符的
比如说:sed -n '/hola/p' 输出所有含有hola的结果
那么hola,holala,holaabc,从这里可以看到匹配模式hola是所有字符串的一个子部分,hola在hola中,也在holala中,也在holaabc中,不在其中的将不会被匹配出来,比如hol肯定不会被匹配出来
否定字符:^ 这个字符也是脱字符,直接用于//中时是脱字符,进行行首字符串匹配,用于[^string]中时是否定字符,意思是不包含string的所有字符串
特殊字符类
用一个单词代表一类字符
[[:alpha:]]:匹配任意大写或小写字母
[[:alnum:]]:匹配任意字母或数字
[[:blank:]]:空白,匹配空格或者制表符
[[:digit:]]:匹配0~9之间的数字
[[:lower:]]:匹配任意小写字母
[[:upper:]]:匹配任意大写字母
[[:print:]]:匹配任意可以打印的字符
[[:punct:]]:匹配标点符号
[[:space:]]:匹配空白符号:空格,制表符,NL,VT,FF,CR
通配符:*这个通配符,必须放在某个字符后面,表示该字符出现0次或者多次,出现0次时候e和0一起e0即表示不出现,所以下面的命令可以匹配出来
hadoop@sdp15:~/RE> echo "ik"|sed -n '/ie*k/p'
ik
在可能多余的字符后面加一个星号,表示允许接受错误的单词,不能把两个及两个以上的*同时使用,即:**是非法的
也可以把点号.和星号*放在一起,匹配任何一种字符串:即.*
echo "ik"|sed -n '/i.*k/p'
也可以把*作用于一个范围内的字符组即[abcde]* 就是[]内的几个字符可以出现0次或者多次,注意:要么出现,并且只出现范围符内的字符,不能出现别的字符,要么不出现
echo "hola"|sed -n '/ho[ab]*a/p'将匹配不出来什么
扩展的正则表达式:
ERE模式
ERE是对正则表达式进行了扩展,sed不能识别,但是gawk可以识别
问号:?
表示匹配0次或者1次,2次及2次以上将不能识别
echo 'hola'|gawk '/holl?a/ {print $0}'
加号:+
表示加号作用于加号紧挨的前面的有并且至少有一次
大括号{}
用大括号来限定模式出现的具体次数,注意此时需要和参数--re-interval联合起来使用
[字符列表]{m}:表示字符列表中的字符必须出现m次
[字符列表]{m,n}表示字符列表中的字符出现m到n次之间,包括m和n次
echo 'beat'|gawk --re-interval '/b[ea]{1}t/ {print $0}'
匹配模式块 命令块
beat
echo 'beat'|gawk --re-interval '/b[ea]{1,2}t/ {print $0}'
管道符号:| 这里的|更应该理解为逻辑符号或|
管道符号可以匹配多个模式
模式1|模式2|模式3
执行时候可以进行多种形式的匹配,通过某种模式都会通过
echo 'this is a dog' | gawk '/cat|dog|fish/ print $0'
//内的|两边没有空格
echo 'this is a cat' | gawk '/[ch]at|dog|fish/ print $0'
小括号()
将匹配表达式进行分组
echo 'this is a cat' | gawk '/(c|h)a(t|p)/'
下面这样写是不对的:
echo 'this is a cat' | gawk '/(c|h)|a|(t|p)/'