Linux Shell 正则表达式

01. 什么是正则表达式?

正则表达式是你所定义的模式模板, Linux工具可以用它来过滤文本。 Linux 工具(比如sed编辑器或gawk程序)能够在处理数据时使用正则表达式对数据进行模式匹配。如果数据匹配模式,它就会被接受并进一步处理;如果数据不匹配模式,它就会被滤掉。

数据流
正则表达式
匹配的数据
滤掉的数据

 
正则表达式模式利用通配符来描述数据流中的一个或多个字符。正则表达式模式含有文本或特殊字符,为sed编辑器和gawk程序定义了一个匹配数据时采用的模板。可以在正则表达式中使用不同的特殊字符来定义特定的数据过滤模式。

02. 正则表达式的类型?

正则表达式是通过正则表达式引擎实现的。正则表达式引擎是一套底层软件,负责解释正则表达式模式并使用这些模式进行文本匹配。

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

  • POSIX 基础正则表达式(basic regular expression, BRE)引擎
  • POSIX 扩展正则表达式(extended regular expression, ERE)引擎

 
在Linux中,常用文本处理工具:

  • sed编辑器,只符合了BRE引擎规范的子集。这是出于速度方面的考虑导致的,因为sed编辑器希望能尽可能快地处理数据流中的文本。
  • gawk程序,用ERE引擎来处理它的正则表达式模式,是awk程序的扩展。
  • grep程序,默认使用BRE基础引擎,-E支持ERE扩展引擎,-P支持Perl语言的正则表达式引擎。egrep = grep -Efgrep = fast grep

03. 基础正则表达式

01. 纯文本

正则表达式并不关心模式在数据流中的位置。它也不关心模式出现了多少次。一旦正则表达式匹配了文本字符串中任意位置上的模式,它就会将该字符串传回Linux工具。

重要的是记住正则表达式对匹配的模式非常挑剔。第一条原则就是:正则表达式模式都区分大小写。这意味着它们只会匹配大小写也相符的模式。

在正则表达式中,你不用写出整个单词。只要定义的文本出现在数据流中,正则表达式就能够匹配。

echo "The books are expensive" | sed -n '/book/p'
The books are expensive

不用局限于在正则表达式中只用单个文本单词,可以在正则表达式中使用空格和数字。

echo "This is line number 1" | sed -n '/ber 1/p'
This is line number 1

这是用来查看文本文件中空格问题的好办法。

sed -n '/ /p' data1
This is a line with too many spaces.
02. 特殊字符

在正则表达式中定义文本字符时有一些特例。有些字符在正则表达式中有特别的含义。

正则表达式识别的特殊字符包括:

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

如果要用某个特殊字符作为文本字符,就必须转义。在转义特殊字符时,你需要在它前面加一个特殊字符来告诉正则表达式引擎应该将接下来的字符当作普通的文本字符。这个特殊字符就是反斜线(\)。

sed -n '/\$/p' data2
The cost is $4.00
echo "\ is a special character" | sed -n '/\\/p'
\ is a special character

尽管正斜线不是正则表达式的特殊字符,但如果它出现在sed编辑器或gawk程序的正则表达式中,你就会得到一个错误。

echo "3 / 2" | sed -n '///p'
sed: -e expression #1, char 2: No previous regular expression

要使用正斜线,也需要进行转义。

echo "3 / 2" | sed -n '/\//p'
3 / 2
03. 锚字符

默认情况下,当指定一个正则表达式模式时,只要模式出现在数据流中的任何地方,它就能匹配。有两个特殊字符可以用来将模式锁定在数据流中的行首或行尾。

锁定在行首

脱字符(^)定义从数据流中文本行的行首开始的模式。如果模式出现在行首之外的位置,正则表达式模式则无法匹配。

echo "The book store" | sed -n '/^book/p'

echo "Books are great" | sed -n '/^Book/p'
Books are great

如果你将脱字符放到模式开头之外的其他位置,那么它就跟普通字符一样,不再是特殊字符了:

echo "This ^ is a test" | sed -n '/s ^/p'
This ^ is a test

锁定在行尾

特殊字符美元符($)定义了行尾锚点。将这个特殊字符放在文本模式之后来指明数据行必须以该文本模式结尾。

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

组合锚点

假定你要查找只含有特定文本模式的数据行。

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

将两个锚点直接组合在一起,之间不加任何文本,这样过滤出数据流中的空白行。

从文档中删除空白行的有效方法。

sed '/^$/d' data5
This is one test line.
This is another test line.
04. 点号字符

特殊字符点号用来匹配除换行符之外的任意单个字符。它必须匹配一个字符,如果在点号字符的位置没有字符,那么模式就不成立。

cat data6
This is a test of a line.
The cat is sleeping.
That is a very nice hat.
This test is at line four.
at ten o'clock we'll go home.

sed -n '/.at/p' data6
The cat is sleeping.
That is a very nice hat.
This test is at line four.
05. 字符组

点号特殊字符在匹配某个字符位置上的任意字符时很有用。但如果你想要限定待匹配的具体字符呢?在正则表达式中,这称为字符组。可以定义用来匹配文本模式中某个位置的一组字符。字符组中必须有个字符来匹配相应的位置。

sed -n '/[ch]at/p' data6
The cat is sleeping.
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
echo "yeS" | sed -n '/[Yy][Ee][Ss]/p'
yeS

字符组不必只含有字母,也可以在其中使用数字。

sed -n '/[0123]/p' data7
This line has 1 number on it.
This line a number 2 on it.

字符组的一个极其常见的用法是解析拼错的单词,比如用户表单输入的数据。

sed -n '
/maint[ea]n[ae]nce/p
/sep[ea]r[ea]te/p
' data9
I need to have some maintenence done on my car.
I'll pay that in a seperate invoice.
After I pay for the maintenance my car will be as good as new.
06. 排除型字符组

通过排除型字符组,正则表达式模式会匹配c或h之外的任何字符以及文本模式。

sed -n '/[^ch]at/p' data6
This test is at line four.
07. 区间

可以用单破折线符号在字符组中表示字符区间。只需要指定区间的第一个字符、单破折线以及区间的最后一个字符就行了。

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

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

sed -n '/[a-ch-m]at/p' data6
The cat is sleeping.
That is a very nice hat.
08. 特殊的字符组

一些固定的特殊字符组。

组 				描 述
[[:alpha:]] 	匹配任意字母字符,不管是大写还是小写
[[:alnum:]] 	匹配任意字母数字字符0~9、 A~Z或a~z
[[:blank:]] 	匹配空格或制表符
[[:digit:]] 	匹配0~9之间的数字
[[:lower:]] 	匹配小写字母字符a~z
[[:print:]] 	匹配任意可打印字符
[[:punct:]] 	匹配标点符号
[[:space:]] 	匹配任意空白字符:空格、制表符、 NL、 FF、 VT和CR
[[:upper:]] 	匹配任意大写字母字符A~Z
echo "abc" | sed -n '/[[:alpha:]]/p'
abc

echo "abc123" | sed -n '/[[:digit:]]/p'
abc123

echo "This is, a test" | sed -n '/[[:punct:]]/p'
This is, a test
09. 星号

字符后面放置星号表明该字符必须在匹配模式的文本中出现0次或多次。

echo "ik" | sed -n '/ie*k/p'
ik

echo "iek" | sed -n '/ie*k/p'
iek

echo "ieek" | sed -n '/ie*k/p'
ieek

另一个方便的特性是将点号特殊字符和星号特殊字符组合起来。这个组合能够匹配任意数量的任意字符。它通常用在数据流中两个可能相邻或不相邻的文本字符串之间。

echo "this is a regular pattern expression" | sed -n '
> /regular.*expression/p'
this is a regular pattern expression

星号还能用在字符组上。它允许指定可能在文本中出现多次的字符组或字符区间。

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

04. 扩展正则表达式

gawk程序能够识别ERE模式,但sed编辑器不能。

记住, sed编辑器和gawk程序的正则表达式引擎之间是有区别的。 gawk程序可以使用大多数扩展正则表达式模式符号,并且能提供一些额外过滤功能,而这些功能都是sed编辑器所不具备的。但正因为如此, gawk程序在处理数据流时通常才比较慢。

01. 问号

问号表明前面的字符可以出现0次或1次,但只限于此。它不会匹配多次出现的字符。

echo "bt" | gawk '/be?t/{print $0}'
bt
echo "bet" | gawk '/be?t/{print $0}'
bet
echo "beet" | gawk '/be?t/{print $0}'
echo "beeet" | gawk '/be?t/{print $0}'
echo "bt" | gawk '/b[ae]?t/{print $0}'
bt
echo "bat" | gawk '/b[ae]?t/{print $0}'
bat
echo "bot" | gawk '/b[ae]?t/{print $0}'
02. 加号

加号表明前面的字符可以出现1次或多次,但必须至少出现1次。如果该字符没有出现,那么模式就不会匹配。

echo "beeet" | gawk '/be+t/{print $0}'
beeet
echo "beet" | gawk '/be+t/{print $0}'
beet
echo "bet" | gawk '/be+t/{print $0}'
bet
echo "bt" | gawk '/be+t/{print $0}'
echo "bt" | gawk '/b[ae]+t/{print $0}'

echo "bat" | gawk '/b[ae]+t/{print $0}'
bat
echo "bet" | gawk '/b[ae]+t/{print $0}'
bet
echo "beat" | gawk '/b[ae]+t/{print $0}'
beat
echo "beet" | gawk '/b[ae]+t/{print $0}'
beet
echo "beeat" | gawk '/b[ae]+t/{print $0}'
beeat
03. 使用花括号

{ }花括号允许你为可重复的正则表达式指定一个上限。这通常称为间隔

  • m:正则表达式准确出现m次。
  • m, n:正则表达式至少出现m次,至多n次。

默认情况下, gawk程序不会识别正则表达式间隔。必须指定gawk程序的 --re-interval 命令行选项才能识别正则表达式间隔。

echo "bet" | gawk --re-interval '/be{1}t/{print $0}'
bet
echo "bt" | gawk --re-interval '/be{1,2}t/{print $0}'

echo "bet" | gawk --re-interval '/be{1,2}t/{print $0}'
bet
echo "bt" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'

echo "bat" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
bat
echo "bet" | gawk --re-interval '/b[ae]{1,2}t/{print $0}'
bet
04. 管道符号

管道符号允许你在检查数据流时,用逻辑OR方式指定正则表达式引擎要用的两个或多个模式。如果任何一个模式匹配了数据流文本,文本就通过测试。如果没有模式匹配,则数据流文本匹配失败。

echo "The cat is asleep" | gawk '/cat|dog/{print $0}'

查找正则表达式cat或dog。正则表达式和管道符号之间不能有空格,否则它们也会被认为是正则表达式模式的一部分。

管道符号两侧的正则表达式可以采用任何正则表达式模式(包括字符组)来定义文本。

echo "He has a hat." | gawk '/[ch]at|dog/{print $0}'
05. 表达式分组

正则表达式模式也可以用圆括号进行分组。当你将正则表达式模式分组时,该组会被视为一个标准字符。

echo "Sat" | gawk '/Sat(urday)?/{print $0}'
Sat
echo "Saturday" | gawk '/Sat(urday)?/{print $0}'
Saturday

将分组和管道符号一起使用来创建可能的模式匹配组是很常见的做法。

echo "cat" | gawk '/(c|b)a(b|t)/{print $0}'
cat
echo "cab" | gawk '/(c|b)|(b|t)/{print $0}'
cab

05. 正则表达式实战

01. 统计目录下的文件数
[root@ufo130 ~]# 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
02. 验证电话号码
[root@ufo130 ~]# cat isphone
#!/bin/bash
# script to filter out bad phone numbers
gawk --re-interval '/^\(?[2-9][0-9]{2}\)?(| |-|\¬
[0-9]{3}( |-|\.)[0-9]{4}/{print $0}'
echo "317-555-1234" | ./isphone
03. 解析邮件地址
^([a-zA-Z0-9_\-\.\+]+)@([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$

你可能感兴趣的:(Linux)