Skip to content

正则表达式

正则表达式 (xusenfeng.github.io)

什么是正则表达式

定义

正则表达式是你所定义的模式模板,Linux工具可以用它来过滤文本。

正则表达式模式利用通配符来描述数据流中的一个或多个字符。Linux中有很多场景都可以使用通配符来描述不确定的数据。

正则表达式的类型

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

常见的引擎:

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

大多数Linux工具都至少符合POSIX BRE引擎规范,能够识别该规范定义的所有模式符号。遗憾的是,有些工具(比如sed编辑器)只符合了BRE引擎规范的子集。这是出于速度方面的考虑导致的,因为sed编辑器希望能尽可能快地处理数据流中的文本

gawk程序用ERE引擎来处理它的正则表达式模式

定义BRE模式

纯文本

正则表达式

  • 不关心模式在数据流中的位置
  • 也不关心次数
  • 匹配大小写
  • 不用写出整个单词, 只要能够匹配就行
  • 可以有空格数字等

特殊字符

有些字符在正则表达式中有特别的含义。如果要在文本模式中使用这些字符,结果会超出你的意料

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

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

不能在文本模式中单独使用这些字符

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

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/19$ sed  -n '/\$/p' data5.txt 
The cost is $4.00
jiao@jiao-virtual-machine:~/桌面/linux-shell/19$ cat data5.txt 
The cost is $4.00
bash
echo "3 / 2" | sed -n '///p' 
sed: -e expression #1, char 2: No previous regular expression

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

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

锚字符

有两个特殊字符可以用来将模式锁定在数据流中的行首或行尾

锁定在行首

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

脱字符会在每个由换行符决定的新数据行的行首检查模式

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/19$ echo "Books are great" | sed -n '/^Book/p'
Books are great

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

如果指定正则表达式模式时只用了脱字符,就不需要用反斜线来转义。但如果你在模式中先指定了脱字符,随后还有其他一些文本,那么你必须在脱字符前用转义字符

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/19$ echo "This is ^ a test" | sed -n '/^/p'
This is ^ a test
jiao@jiao-virtual-machine:~/桌面/linux-shell/19$ echo "This is ^ a test" | sed -n '/^ a/p'
jiao@jiao-virtual-machine:~/桌面/linux-shell/19$ echo "This is ^ a test" | sed -n '/\^ a/p'
This is ^ a test

锁定在行尾

特殊字符美元符($)定义了行尾锚点。

组合锚点

  • 把两种合起来使用, 查找整行
  • 直接相连查找空白的行
bash
$ cat data5 
This is one test line.

This is another test line.
$ sed '/^$/d' data5 			# 删除空白行
This is one test line. 
This is another test line.

点号字符

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

字符组

但如果你想要限定待匹配的具体字符呢?在正则表达式中,这称为字符组

[ch]at    # 匹配cat和hat

使用方括号来定义一个字符组。方括号中包含所有你希望出现在该字符组中的字符

在不太确定某个字符的大小写时,字符组会非常有用

也可以用来排除输入错误的单词

排除型字符组

反转字符组的作用, 寻找组中没有的字符

[^ch]at    除了cat和hat以外的

区间

使用单破折号

[0-9][a-z][a-ch-m]

可用于数字、字母

特殊的字符组

BRE还包含了一些特殊的字符组,可用来匹配特定类型的字符

描述
[[: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

星号

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

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

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

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

扩展正则表达式

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

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

问号

类似于星号, 但是只能重复0或1次

可以和字符组一块使用

加号

前面的字符可以出现一次或者多次, 但是最少出现一次

使用于字符组

花括号

你为可重复的正则表达式指定一个上限。这通常称为间隔

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

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

当为一的时候只能出现一次才能匹配得上

bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/19$ echo "bt" | gawk --re-interval '/be{1}t/{print $0}'
jiao@jiao-virtual-machine:~/桌面/linux-shell/19$ echo "bet" | gawk --re-interval '/be{1}t/{print $0}'
bet

可以匹配字符组

管道符号

OR方式指定正则表达式引擎要用的两个或多个模式。如果任何一个模式匹配了数据流文本,文本就通过测试。如果没有模式匹配,则数据流文本匹配失败

bash
expr1|expr2|...

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

表达式分组

正则表达式模式也可以用圆括号进行分组。当你将正则表达式模式分组时,该组会被视为一个标准字符。可以像对普通字符一样给该组使用特殊字符。

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

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

正则表达式实战

目录文件计数

bash
  1 #!/bin/bash                                                                           
  2 
  3 mypath=$( echo $PATH | sed 's/:/ /g' )
  4 count=1
  5 for directory in $mypath		# 获取文件目录
  6 do
  7     check=$(ls $directory)		# 获取文件列表
  8     for item in $check
  9     do
 10         count=$[ $count+1 ]
 11     done
 12     echo "$directory - $count"
 13     count=0
 14 done

解析邮箱地址

username@hostname值可用字母数字字符以及以下特殊字符

  • 单破折号
  • 加号
  • 下划线

服务器名和域名也必须遵照严格的命名规则,只允许字母数字字符以及以下特殊字符

  • 下划线
bash
^([a-zA-Z0-9_\+\-\.]+)@([a-zA-Z0-9_\.\-]+)\.([a-zA-Z]{2,5})$