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

Shell脚本高级编程 二 正则表达式

2017-05-18 16:05 811 查看
在shell脚本中成功使用sed编辑器和gawk程序的关键在于熟练使用正则表达式。这能够是你,从大量数据中过滤出特定数据。

(1)什么是正则表达式

①定义

正则表达式是我们自己定义的、Linux工具用它来过滤文本的模式模板。Linux工具(比如,sed编辑器或gawk程序)能够在数据流向工具时对数据进行正则表达式模式匹配。如图所示:



例如,下面是正则表达式的一个应用,其中*是通配符:

$ls -al test*
-rw-rw-r-- 1 xiaoyu1 xiaoyu1  8 May 18 14:05 test
-rw-rw-r-- 1 xiaoyu1 xiaoyu1 31 May 17 09:37 testfile


②正则表达式的类型

使用正则表达式最大的问题在于有不止一种类型的正则表达式。Linux中的不同程序可能会用不同类型的正则表达式。这包括各种应用程序,比如编程语言(Java、Perl和Python)、Linux工具(sed编辑器、gawk程序、grep工具等),以及主流应用(如MySQL和PostgreSQL数据库服务器)。

正则表达式是用正则表达式引擎(regular expression engine)实现的,它是解释正则表达式模式并使用这些模式进行文本匹配的底层软件。

在Linux中,有两种流行的正则表达式引擎:

POSIX基本正则表达式(BRE)引擎;

POSIX扩展正则表达式(ERE)引擎。

大多数Linux工具都至少符合POSIX BRE 引擎规范,能够识别它定义的所有模式符号。但,有些工具(如sed编辑器)只实现了BRE引擎规范的子集。这是速度的要求导致的,sed编辑器希望快速地处理数据流中的文本。

POSIX BRE 引擎通常为依赖正则表达式做文本过滤的编程语言所支持。它为常见模式提供了高级模式符号和特殊符号,比如匹配数字、单词以及按字母排序的字符。gawk 程序用ERE引擎来处理它的正则表达式模式。

由于正则表达式的实现方法很多,很难用一个简洁的描述来涵盖所有可能的正则表达式。所以,这里我们只讨论最常见的正则表达式并演示如何在sed编辑器和gawk程序中使用它们。

(2)定义 BRE 模型

①纯文本

如何在sed编辑器中使用标准文本字符串来过滤数据,如例子所示:

$echo "This is a test" | sed -n '/test/p'
This is a test
$echo "This is a Test" | sed -n '/test/p'
$
$echo "This is a testing" | sed -n '/test/p'
This is a testing


上例子可以得出如下结论:

正则表达式不管模式在数据流中的位置和出现次数,一旦表达式匹配了文本字符串中的模式,它就会将该字符串传回Linux工具;

正则表达式模式都是区分大小写的;

正则表达式模式时部分匹配的;

正则表达式中可以使用空格和数字作为模式匹配文本,空格在整个表达式中会跟其他字符一样对待,如下例所示:

$echo "This is a testing" | sed -n '/ /p'
This is a testing
$echo "Thisisatesting" | sed -n '/ /p'
$echo "This is a testing" | sed -n '/  /p'
$


其中,第一个和第二个命令的匹配模式是一个空格;第三个命令的匹配模式是两个空格。这是用来查看文本文件中空格问题的好办法。

②特殊字符

由于我们会在正则表达式中使用文本字符,就必须注意定义文本字符时的一些特例。正则表达式识别的特殊字符包括:

.*[]^${}+?()

后续会介绍这些特殊字符在正则表达式中的作用,但现在只需记住不能在文本模式中单独使用这些字符即可。

如需使用某个特殊字符作为文本字符,需要转义。在特殊字符前添加反斜线(\),则该字符会被当做普通文本字符对待。

$echo "This is a $ testing" | sed -n '/\$/p'
This is a $ testing
$echo "This is a $ \testing" | sed -n '/\\/p'
This is a $ \testing


上例中,查找美元符号需要转义;同样查找\也需要转义。

注意,尽管斜线不是正则表达式的特殊字符,但在sed编辑器或gawk程序的正则表达式中使用会出现错误。所以,使用斜线也需要转义:

$echo "3 / 2" | sed -n '///p'
sed: -e expression #1, char 3: unknown command: `/'
$echo "3 / 2" | sed -n '/\//p'
3 / 2


③锚字符

默认情况下,正则表达式模式匹配在数据流中任意位置的数据。有两个特殊字符可以用来将模式锁定在数据流中的行首或行尾。

锁定在行首

脱字符(caret character,^)定义从文本行的行首开始匹配。如果模式位于非行首位置,则匹配不成功。

脱字符,必须放在正则表达式中指定的模式前面:

$echo "This is a testing" | sed -n '/^is/p'
$echo "This is a testing" | sed -n '/^This/p'
This is a testing


如果脱字符放在模式中的非开头位置,它会跟普通字符一样对待:

$echo "This is a^ testing" | sed -n '/a/p'
This is a^ testing
$echo "This is a^ testing" | sed -n '/a^/p'
This is a^ testing


锁定在行尾

跟行首查找相对的就是行尾查找。美元符号($)定义行尾锚点。

$echo "This is a good book" | sed -n '/book$/p'
This is a good book
$echo "This book is good" | sed -n '/book$/p'
$
$echo "This is a good books" | sed -n '/book$/p'
$


注意,上例中将book改为复数形式,则不能在匹配正则表达式了。因为,文本模式必须是模式要匹配的行的最后部分。

组合锚点

在一些场景下,我们需要在同一行中将行首锚点和行尾锚点组合使用。

场景一:查找含有特定文本模式的数据行,

$cat data4
this is a  test using...
I said this is a  test
this is a  test
yes,this is a  test

$sed -n '/^this is a  test$/p' data4
this is a  test


场景二,将两个锚点直接组合在一起,中间不添加如何文本,可以过滤数据流中的空白行。如下例:

$cat data5
this is a  test using...

yes,this is a  test
$sed '/^$/d' data5
this is a  test using...
yes,this is a  test


④点字符

点字符用来匹配任意的单字符,除了换行符。但点字符必须匹配一个字符,如果点字符的位置没有字符,那该模式不能被匹配。例子如下:

```
$cat data6
This is a test.
The cat is ...
That is a very nice hat.
This test is at line four.
at five line.
$sed -n '/.at/p' data6
The cat is ...
That is a very nice hat.
This test is at line four.
```


⑤字符组

点字符允许你匹配任意单个字符。如何只匹配给定的某些字符呢?在正则表达式中,这称为字符组(character class)。我们使用方括号([])定义字符组,在方括号中指定你要匹配的某些字符。如下例子所示:

$sed -n '/[ch]at/p' data6
The cat is ...
That is a very nice hat.


字符组在你不确定某个字符大小写情况时,非常有用:

$echo "Yes" | sed -n '/[Yy]es/p'
Yes
$echo "yes" | sed -n '/[Yy]es/p'
yes


也可以在单个表达式中用多个字符组:

$echo "Yes" | sed -n '/[Yy][Ee][Ss]/p'
Yes
$echo "YeS" | sed -n '/[Yy][Ee][Ss]/p'
YeS


字符组中也可以使用数字:

$cat data7
line 1.
hello
line 2.
$sed -n '/[123]/p' data7
line 1.
line 2.


应用场景一,检查数字是否是正确格式化了,比如电话号码和邮编。但使用时容易出错,如下例所示:

$cat data8
60633
46201
223001
4353
22203
$sed -n '
> /[0123456789][0123456789][0123456789][0123456789][0123456789]/p
> ' data8
60633
46201
223001
22203


上例中,尽管我们过滤掉了过短而不可能是邮编的数字。但是超过6位的还存在,因为我们只定义了5个字符组。

要确保只匹配5位数,必须将匹配字符和其他字符分开,可以使用空格或指明它们就在行首和行尾的方法:

$sed -n '
> /^[0123456789][0123456789][0123456789][0123456789][0123456789]$/p
> ' data8
60633
46201
22203


字符组应用场景二,解析拼写错误的单词,比如用户表单输入的数据。

$sed -n '
> /maint[ea]n[ae]nce/p
> /sep[ea]r[ea]te/p
> ' data9
I need to maintenence done on my car.
I'll paly in a seperate.
After I pay for the maintenance my ...


本例中的两个sed打印命令利用正则表达式字符组来帮助找到文本中拼写错误的单词maintenance和separate。同样的正则表示模式也能匹配正确拼写的maintenance。

⑥排除字符组

在正则表达式中,使用脱字符(^)反转字符组的作用,即匹配不含组中字符的模式。只需要在字符组开头加个脱字符:

$cat data6
This is a test.
The cat is ...
That is a very nice hat.
This test is at line four.
at five line.
$sed -n '/[^ch]at/p' data6
This test is at line four.


注意,空格也不在字符组的范围内;但即使是排除,也要求字符组必须匹配一个字符,因此at开头的行未能匹配模式。

⑦使用区间

如我们前面演示的邮编的例子,我们必须在每个字符组中列出所有可能的数字。但我们可以使用区间来代替这一方式。

区间的写法是:[第一个字符-最后一个字符]。根据Linux系统所采用的字符集,正则表达式包含这个指定区间内的任意字符。

邮编例子可改写为:

$sed -n '/^[0-9][0-9][0-9][0-9][0-9]$/p' data8
60633
46201
22203


指定区间,也适用于字母:

$sed -n '/[c-z]at/p' data6
The cat is ...
That is a very nice hat.


还可以在单个字符组中指定多个不连续区间:

$sed -n '/[a-ch-m]at/p' data6
The cat is ...
That is a very nice hat.


注意,要按字符的ASCII码中的顺序指定,比如c-a这样的指定方式是无效的。

⑧特殊字符组

除了自己定义的字符组外,BRE还包含可用来匹配特殊字符类型的特殊字符组。如下表所示:



可以在正则表达式模式中将特殊字符组像普通字符组一样使用:

$echo "aBc" | sed -n '/a[[:alpha:]]c/p'
aBc
$echo "abc" | sed -n '/a[[:alpha:]]c/p'
abc


⑨星号

在字符后面放置*,说明该字符将会在匹配模式中出现0次或多次:

$echo "ab" | sed -n '/ae*b/p'
ab
$echo "aeb" | sed -n '/ae*b/p'
aeb
$echo "aeeeeb" | sed -n '/ae*b/p'
aeeeeb


这个模式符号应用场景,检查拼写错误或在不同语言中有拼写变种的单词。例如:

$echo "I'm getting a color TV" | sed -n '/colou*r/p'
I'm getting a color TV
$echo "I'm getting a colour TV" | sed -n '/colou*r/p'
I'm getting a colour TV


将.和*组合使用的场景,这个组合提供了匹配任意多个任意字符的模式。例如:

$echo "This is a good student." | sed -n '/a.*student/p'
This is a good student.
$echo "This is a bad student." | sed -n '/a.*student/p'
This is a bad student.


*还可以用在字符组上,它允许指定可能在文本中出现0次或多次的一组字符或一个字符区间:

$echo "bt" | sed -n '/b[ae]*t/p'
bt
$echo "bat" | sed -n '/b[ae]*t/p'
bat
$echo "baeaeeat" | sed -n '/b[ae]*t/p'
baeaeeat


(3)扩展正则表达式

POSIX ERE模式包括一些Linux应用和工具使用的若干额外符号。gawk程序能够识别ERE模式,但sed编辑器不能。

待补充。。。

(4)实用中的正则表达式

接下来将演示shell脚本中常见的一些正则表达式的例子。

目录文件数统计

首先,列出PATH变量中的所有目录,要意识到PATH中的每个路径都是由冒号分隔的。要获取可以在脚本中使用的目录列表,需要用空格来替换冒号。然后,使用for语句遍历每个目录中的所有文件,为每个文件将计数器+1.

$cat countfiles
#!/bin/bash
# count number of files in your PATH

mypath=`echo $PATH | sed 's/:/ /g'`
count=0

for directory in $mypath
do
check=`ls $directory`
for item in $check
do
count=$[ $count + 1 ]
done
echo "$directory - $count"
count=0
done

$./countfiles
/opt/node/bin - 3
/usr/local/bin - 1
/bin - 1158
/usr/bin - 1158
/usr/local/sbin - 0
/usr/sbin - 550
/usr/local/java/jdk1.7.0_75/bin - 47
/usr/local/java/jdk1.7.0_75/jre/bin - 14
/usr/local/hadoop/bin - 11


验证电话号码

由gawk程序完成。待补充。。。

解析邮件地址

待补充。。。

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