您的位置:首页 > 运维架构 > Shell

shell编程笔记-正则表达式

2014-01-21 13:15 176 查看

第四章 正则表达式

什么是正则表达式

在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。





正则表达式的广泛应用
正则表达式在unix/linux系统中得到广泛的应用,强化了工具本身的功能。常见的UNIX下支持正则表达式的工具有:

》用于匹配文本行的grep工具族:

》用于改变输入流的sed流编辑器(streamediter);

》用于处理字符串的语言,如awk, python, perl, tcl等语言;

》文件查看程序,或分页程序,如mare. page, less等。

》文本编辑器,如ed, vi,emacs, vim等·





如何学习正则表达式
实例一

匹配当前存在 file单词并且在这个单词后还有一个file的句子



\bfile\b.*\bfile\b



Copy一段

假如你要找的是hi后面不远处跟着一个Lucy,你应该用\bhi\b.*\bLucy\b。

这里,.是另一个元字符,匹配除了换行符以外的任意字符。*同样是元字符,不过它代表的不是字符,也不是位置,而是数量——它指定*前边的内容可以连续重复使用任意次以使整个表达式得到匹配。因此,.*连在一起就意味着任意数量的不包含换行的字符。现在\bhi\b.*\bLucy\b的意思就很明显了:先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词。

换行符就是'\n',ASCII编码为10(十六进制0x0A)的字符。

如果同时使用其它元字符,我们就能构造出功能更强大的正则表达式。比如下面这个例子:

0\d\d-\d\d\d\d\d\d\d\d匹配这样的字符串:以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字(也就是中国的电话号码。当然,这个例子只能匹配区号为3位的情形)。

这里的\d是个新的元字符,匹配一位数字(0,或1,或2,或……)。-不是元字符,只匹配它本身——连字符(或者减号,或者中横线,或者随你怎么称呼它)。

为了避免那么多烦人的重复,我们也可以这样写这个表达式:0\d{2}-\d{8}。这里\d后面的{2}({8})的意思是前面\d必须连续重复匹配2次(8次)。





如果需要更精确的说法,\b匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)\w。





如何实践正则表达式
我们不是机器,总会犯错误。一般情况下,正则表达式不可能一次写正确。这就需要我们不断修改正则表达式,不断通近那个正确的写法,直到恰好达到我们想要的目的。







NOTE

历史上曾经出现过3种grep,可以用于匹配文本。它们是:

Grep 最早的文本匹配程序。使用PQSIX支持的基本正则表达式(Basic Regular Expression, BRE )

Egrep 扩展的grep(Extend grep)。使用扩展正则表达式〔Extended Regular Expression, ERE )

Fgrep 快速grep( Fast grep)。这个版本用于匹配固定字符串而不是正则表达式。

在I942年发布的PDS1X标准中,3个prep版本合而为一,POSIX可以通过参数来支持多个正则表达式模式,无论是BRE还是ERE,fgrep和egrep还是可以在所有的unix/inux系统上使用,但是被标记为deprecated(不推荐〕





正则基础

元字符
正则表达式是描述某种匹配规则的工具。

正则中有两种字符一种基本字符(没有任何意义),一种元字符(赋予了表达匹配的某些含义)



POSIXBRE和ERE都支持的meta字符



^
BRE,ERE
锚定行或者字符串的开始,如"^grep" 匹配所有以grep开头的行。

BRE:仅在正则表达式结尾处具有特殊含义;

ERE:在正则表达式的任何地方都有特殊含义。
$
BRE,ERE
锚定行或者字符串的结束,如"grep$" 匹配所有以grep结束的行。

BRE:仅在正则表达式结尾处具有特殊含义;

ERE:在正则表达式的任何地方都有特殊含义。
.
BRE,ERE
匹配一个非换行的任何单个字符(除NUL)。

如:‘gr.p'匹配gr后加一个任意字符后,然后p
*
BRE,ERE
匹配其前的任何数目或没有的单个字符,

例: . 表示任一字符, 则 .* 匹配任一字符的任意长度
[]
BRE,ERE
匹配方括号内的任一字符,其中可用连字符(-)指的连续字符的范围

;^符号苦出现在方括号的第一个位置,则表示匹配不在列表中的任一字符,
POSIXBRE中才有的字符:

\{n,m\}
区间表达式,匹配在它前面的单个字符重现的次数区别。

\{n\}指重现n次;\{n,m\}指重现n至m次;
\( \)
保留空间,可以将最多9个独立的子模式存储在单个模式中。

如\(ab\).*\1 : 指匹配ab组合的两次重现,中间可存在任意数目的字符。
\n
重复在\(与\)方括号内第n个子模式至此点的模式。
POSIXERE中才有的字符:



{n,m}
与BRE的\{n,m\}功能相同
+
匹配前面正则表达式的一个或多个扩展
?
匹配前面正则表达式的零个或一个扩展
|
匹配|符号前或后的正则表达式
( )
匹配方括号括起来的正则表达式群
Grep程序支持的meta字符plus



\<
锚定单词的开始,如:“\<grep” 匹配以grep开头的单词的行
\>
锚定单词的结束,

如:“grep\>”匹配包含以grep结尾的单词的行
\w
匹配文字和数字字符.也就是[A-Za-z0-9],

如’G\w*p‘匹配以G后跟零个或多个文字或数字字符.然后是p
\W
\w的反之形式,匹配一个或多个非单词字符,如点号、句号等
\b
单词锁定符,如:\bgrep\b只匹配grep
实例



//只显示以a开头的文件

[houchangren@ebsdi-23260-oozie shell]$ ls |grep '^a'
add.sh
a.sh




//显示a开头的文件中包含hadoop的行

[houchangren@ebsdi-23260-oozie shell]$grep  'hadoop'  a*
a.sh:HADOOP_IN_PATH="/usr/lib/hadoop/bin/hadoop"
a.sh:HADOOP=$HADOOP_HOME/bin/hadoop


//显示a.sh文件中包含hadoop的行

[houchangren@ebsdi-23260-oozie shell]$grep  'hadoop'  a.sh
HADOOP_IN_PATH="/usr/lib/hadoop/bin/hadoop"
HADOOP=$HADOOP_HOME/bin/hadoop




//显示连续五个a-z的字符在一行的行

[houchangren@ebsdi-23260-oozie shell]$ grep'[a-z]\{5\}' a.sh
HADOOP_IN_PATH="/usr/lib/hadoop/bin/hadoop"
 HADOOP_DIR=`dirname "$HADOOP_IN_PATH"`/..
HADOOP=$HADOOP_HOME/bin/hadoop
HADOOP_VERSION=$($HADOOP version | awk '{if(NR == 1) {print $2;}}');


//显示如果test被匹配,es就被存储到内存中,并标记为1.然后搜索任意个字符(.*),这些字符后面跟着另外一个es (\1),找到就显示该行。\1是取占位括号中的值。

[houchangren@ebsdi-23260-oozie shell]$ cata.txt
I'm test; test;
so you don't worry!
[houchangren@ebsdi-23260-oozie shell]$ grep't\(es\)t.*\1' a.txt
I'm test; test;




为了在不同国家的字符编码中保持一至,POSIX(The PortableOperating System Interface)增加了特殊的字符类,如[:alnum:]是A-Za-z0-9的另·个写法。要把它们放到[]号内才能成为正则表达式,如[A-Za-z0-9]或[[:alnum: ]] 在Linux下的grep除fgrap外,都支持POSIX的字符类。方括号表达式除了上面提到的字符外,还支持其他的组成形式:

1. posix字符集



[:alnum:]
文字数字字符
[:alpha:]
文字字符
[:blank:]
空格与定位字符
[:cntrl:]
控制字符
[:digit:]
数字字符
[:graph:]
非空格字符
[:lower:]
小写字母字符
[:print:]
可显示的字符
[:punct:]
标点符号字符
[:space:]
空格字符
[:upper:]
大写字母字符
[:xdigit:]
16进制数字
2. 排序符号

指将多个字符视为一个符号,如[.cn.]即将cn视为一个符号

3. 等价字符集

认为多个字符相等,如[=e=]在法文的locale里,可匹配于多种与e相似的字符











正则表达式允许将POSIX字符集与其他字符集混用。如[[:alpha:]!]匹配任意一个英文字母或者感叹号(!)。



//匹配或者数字或者!

[houchangren@ebsdi-23260-oozie shell]$ grep -E'[[:digit:]!]+' a.txt

so you don't worry!





单个字符
匹配单个字符的方式有4种:一般字符、转义的meta字符、.(点号)meta字符,和方括号表达式。

1. 一般字符

例如:abc 就是 匹配abc

2. 转义的meta字符

例如:\*匹配 * \[\] 匹配[]

3. .(点号)字符

例如: .bc 匹配 abc,dbc等等

4. 方括号表达式

例如:[cC]hina 只匹配 china 和Chnia

[^abc]d 匹配除了abc之外的任何小写字母

单个表达式匹配多个字符


文本的匹配锚点

^ 和& 开头和结尾的表示



运算符优先级


BRE的运算优先

运算符
含义
[..][==][::]
方括号符号
\meta
转义的meta字符
[]
方括号表达式
\(\)\n
后向引用表达式
*\{\}
区间表达式和星号表达式
无符号
连续
^&
锚点
ERE的运算优先



运算符
含义
[..][==][::]
方括号符号
\meta
转义的meta字符
[]
方括号表达式
()
分组
*+?{}
重置前置的正则表达式
无符号
连续
^&
锚点
|
交替
更多差异


1. 向后引用

BRE中提供了一种机制名为“后向引用”backreferences,含义是:匹配的是之前正则表达式选定的部分。我们使用\1一\19来引用之前选定的模式,使用\(和\)括起想要之后引用的部分。

例如:

\(go\).*\1 匹配一行中前后出现两个go

2. 交替

交替是ERE才一有的特性。当使用方括号表达式时,表示可以“匹配这个字符,或者那个字符”,但是无法“匹配这个字符序列或那个字符序列”。当我们需要这个功能时,在ERE中,就用到交替.交替是在不同序列之间用管道符号隔开。例如,you|me匹配you或者me.

交替字符可以和管道符号一样,在一个正则表达式中使用多个来提供多种选择。因为交替字符的优先级最低,所以会一直扩展到新的交替字符,或正则表达式结束为止。

3. 分组



在BRE中,我们使用一些meta字符修饰前置字符,匹配重复的情况。但是这样的操作仅仅

针对单个字符。在ERE中,分组功能能够计meta字符修饰前置字符串。分组符号就是使用(和)

将式子括起来。例如〔go)+匹配一个或多个连续的go.

在使用交替时,分组就非常有用。例如:(Lily|Lucy) will visit my house today.在这个正则表达式中,分组限定了访问我家的是Lily或者是Lucy。









正则表达式的应用

正则表达式之所以如此重要,一个原因是许多程序(包括UNIX/linux下的,和Win下的)

都使用正则表达式为自己提供扩展,以支持更强大的功能。

正则表达式的风格有两种,BRE和ERE,这是历史遗留的产物。egrep风格的正则表达式在

UNIX开发的早期就已经出现,但是Linux的创始人KenThompson觉得不需要在ed编辑器中使

用这样全方位的正则表达式支持,ed的标准后来演化为BRE。

ed又成为grep和sed的基础,故此。grep和sed支持的正则表达式类型是BRE。在pre-V7

时期,egrep被发明出来,egrep是使用的ERE风格正则。但是,当egrep, grep, fgrep合并成

个grep程序时,grep就同时支持不同风格的正则表达式〔通过一E选项支持ERE)o与此同时,egrep

虽然被从POSIX标准中踢出,但是许多UNIX/LINUX发行版本仍然文持egrep命令,而且许多历

史遗留脚本仍然在使用egrep,虽然标准做法应该是grep -E

使用ERE风格n则表达式的部分程序有egrep, awk和lex. lex是一个词法分析器构建程序,

除了特殊情况下,很少在shell编程中用到。

扩展
除了前面提到的BRE和ERE这两种标准正则表达式支持外,许多程序根据需求提供正则表

达式的语法扩展。最常见的扩展是\<和\>,分别匹配单词开头和单词结尾。

单词的开头有两种情况,一种是位于行的起始位置,一种是紧跟在非一单词组成字符后面;同样,单词的结尾也是两种情况;行的末尾和尾随一个非单词组成字符。





GUN支持额外的正则表达式运算符

 
含义
\w
匹配任何单词组成字符,等同[[:alnum:]]
\W
匹配任何非单组成字符,等同[^[:alnum:]]
\<\>
匹配单词的开头和结尾
\b
匹配单词开头和结尾处所找到的空字符o \bword就等同\<word\>
\B
匹配两个单词组成字符间的空字符串
\’\`
分别匹配emacs缓冲区的开头和结尾。GNU程序通常将它们视为与^和S同义
研究罗马数字

有点脑袋大,那天有心情在研究吧。



解析电话号码实例







echo '80055512121234' | grep -E"^[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{4}[^[:digit:]]*[[:digit:]]*$"
echo '800-555-1212' | grep -E"^[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{4}[^[:digit:]]*[[:digit:]]*$"
echo '800-555-1212-1234' | grep -E"^[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{4}[^[:digit:]]*[[:digit:]]*$"
echo 'work 1-(800) 555.1212 #1234' | grep-E "[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{3}[^[:digit:]]*[[:digit:]]{4}[^[:digit:]]*[[:digit:]]*$"


表达式解析一下:



[[:digit:]]{3} 三位数字

[^[:digit:]]* 非数字的任何东西(或者没有)

[[:digit:]]{3} 三位数字

[^[:digit:]]* 非数字的任何东西(或者没有)

[[:digit:]]{4} 四位数字

[^[:digit:]]* 非数字的任何东西(或者没有)

[[:digit:]]* 数字(可没有)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: