shell编程笔记-正则表达式
第四章 正则表达式
什么是正则表达式
在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
正则表达式的广泛应用
正则表达式在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 | 锚定行或者字符串的结束,如"grep$" 匹配所有以grep结束的行。 |
. | BRE,ERE | 匹配一个非换行的任何单个字符(除NUL)。 |
* | BRE,ERE | 匹配其前的任何数目或没有的单个字符, |
[] | BRE,ERE | 匹配方括号内的任一字符,其中可用连字符(-)指的连续字符的范围 |
POSIXBRE中才有的字符:
\{n,m\} | 区间表达式,匹配在它前面的单个字符重现的次数区别。 |
\( \) | 保留空间,可以将最多9个独立的子模式存储在单个模式中。 |
\n | 重复在\(与\)方括号内第n个子模式至此点的模式。 |
POSIXERE中才有的字符:
{n,m} | 与BRE的\{n,m\}功能相同 |
+ | 匹配前面正则表达式的一个或多个扩展 |
? | 匹配前面正则表达式的零个或一个扩展 |
| | 匹配|符号前或后的正则表达式 |
( ) | 匹配方括号括起来的正则表达式群 |
Grep程序支持的meta字符plus
\< | 锚定单词的开始,如:“\<grep” 匹配以grep开头的单词的行 |
\> | 锚定单词的结束, |
\w | 匹配文字和数字字符.也就是[A-Za-z0-9], |
\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:]]* 数字(可没有)
更多推荐
所有评论(0)