[TOC]

前言

Shell正则表达式:使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串,Linux上的一些编辑器就支持
例如:vi, grep, awk ,sed,expr等等工具,因为她们有支持正规表示法,所以这些工具就可以使用正规表示法的特殊字符来进行字符串的处理;

注意事项:

  • 不同的软件在使用上有不同的差异:命令不一样但大部分是相同得.
  • 语系对正则表达式的影响: 由于不同语系的编码数据不同,所以造成不同语系的数据选取结果有所差异。
    #以英文大小写为例,zh_CN.big5 及 C 这两种语系差异如下:
    LANG=C 时: 0 1 2 3 4....ABCDE...Zabcde...z #在使用正则表达式[A-Z]时, LANG=C 的情况下,找到的仅仅是大写字符 ABCD..Z。
    LANG=zh_CN 时:0 1 2 3 4...aAbBcCdD.....zZ #而在 LANG=zh_CN 情况下,会选取到 AbBcCdD.....zZ 字符

因此在使用正则表达式时要特别留意语系,由于我们一般使用的兼容与 POSIX 的标准,因此使用 C 语系;

Shell中的正则表达式组成:

  • 字符类 : 特殊字符POSIX类
  • 特殊符号类 : 元字符
  • 数量限定符

WeiyiGeek.Linux-reg-grep脑图


Shell正则表达式详解

特殊符号类
元字符  描述
\ 将下一个字符标记符、或一个向后引用、或一个八进制转义符
[] 匹配[ abc ]中任意一个字符
- 在[ ]括号里使用,表示字符范围
^ 匹配输入字符串的开始位置,可以在[]和()外和内使用^[abc]表示以什么开头,而在[^abc]表示除此之外
$ 匹配输入字符串的结束位置,^$ 匹配空行
| 将两个匹配条件进行逻辑“或”(Or)运算。

实际案例:

#示例0.即相当于多种编程语言中都有的"转义字符"的概念。
#"\\n"匹配\n "\n"匹配换行符 序列"\\" 匹配"\" 而"\("则匹配"("
echo "[email protected]" | grep -oE "^[0-9]{5,13}@qq\.com" --color #也可以用""
[email protected]


#示例1.匹配行首 ^ [] ()
#如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。
echo "hello world" | grep -oE '^hello'
hello
#匹配手首行字母不对数字与字母产生效果
echo "abc123ABC bcd456BCD" | grep -oE '^[ab]'
a
#匹配括号里面的字符串
echo "abc123ABC bcd456BCD" | grep -E '^(abc)'
abc
#除此abc之外的全部字符
echo "abc123ABC bcd456BCD" | grep -E '[^abc]'
123ABC d456BCD

echo "plain 123 test" | grep -E '[^a-z]'
123

echo "abc123ABC" | grep -E '[A-Z]'
ABC
echo "abc123ABC bcd456BCD" | grep -oE '[ab]' #有a或者b
a
b
b
echo "abc123ABC acd456BCD" | grep -E 'a[bc]' #ab或者ac


#示例2.匹配行尾 $
grep -n '^$' regular_express.txt #查找空行 (常用)
echo "hello world" | grep -E 'world$' #如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。
world


#示例3.例如正则表达式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。
echo "hello helloe hellaa" | grep -oE 'hel(lo|loe)' #并列两个表达式 ^h|^H
hello
helloe


#例如“z|food”能匹配“z”或“food”,“(z|f)ood”则匹配“zood”或“food”
echo "zood food " | grep -oE 'z|food'
z
food
echo "zood food " | grep -oE '(z|f)ood'
zood
food

注意事项:

  • 这个元字符不是所有的软件都支持的


数量限定符
.点 匹配除“\r\n”之外的任何单个字符
* 匹配前面的子表达式任意次 (1-n)
+ 匹配前面的子表达式一次或多次(大于等于1次)(1-n)
? 匹配前面的子表达式零次或一次 (0-1)
#当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串

{n} n是一个非负整数,匹配确定的n次
{n,} n是一个非负整数,至少匹配n次
{,m} n是一个非负整数,至多匹配m次
{n,m} m和n均为非负整数,其中n<=m,最少匹配n次且最多匹配m次。
{i} {i,j} 匹配指定数目的字符,这些字符是在它之前的表达式定义的

基础示例:

# * = {0,}
echo "helloooo world" | grep -E '*o'
echo "helloooo world" | grep -oE 'o*' #zo*能匹配“z”,“zo”以及“zoo” , * 等价于{0,}
oooo
o

echo "helloooo world" | grep -oE 'lo*' #特殊点对比
l
loooo
l
echo "helloooo world" | grep -oE 'l|o*' #特殊点 匹配l或者o零次或者一次
l
l
oooo
o
l

# + = {1,}
echo "helloooo world" | grep -E 'lo+' #“zo+”能匹配“zo”以及“zoo”,但不能匹配“z” , + 等价于{1,}
echo "helloooo world" | grep -oE 'lo+'
loooo


# ? = {0,1}
echo "helloooo world" | grep -oE 'lo?' # “do(es)?” 可以匹配“do”或“does”中的“do” , ? 等价于{0,1} - 理解这个就可以理解上面的grep -oE 'lo*'
l
lo
l
echo "does doee" | grep -E 'do(es)?' # 匹配括号中的字符0次或者一次
does doee

# . = {1}
#匹配除“\r\n”之外的任何单个字符,要匹配包括“\r\n”在内的任何字符,请使用像“[\s\S]”的模式。
echo -e "\n\rloo\n\r" | grep -oE "lo." #loo
echo -e "\n\rloo\n\r" | grep -oE "o." #oo


# --------------- 分割线--------------------

#例如“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o,或者o{n},n的倍数个o
echo "dooes" | grep -E 'o{2}' #匹配2次o,或者2的倍数个o
oo
echo "dooes" | grep -oE 'o{1}' #匹配1次o,或者全部o 特殊
o
o

echo "fooooood" | grep -oE 'fo{3}'
fooo


#例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o
echo "dooooes" | grep -oE 'o{1,}' #“o{1,}”等价于“o+” ,“o{0,}”则等价于“o*”
echo "dooooes" | grep -oE 'o{0,}'
oooo
echo "fooooood" | grep -oE 'fo{3,}'
foooooo


#例如,“o{,0}”不能匹配“Bob”中的“o”,“o{,n}”能匹配“foooood”中的所有o
echo "dooooes" | grep -E 'o{,1}' #至少匹配1次或者多次
echo "dooooes" | grep -E 'o{,2}'
echo "dooooo1osadasdoes" | grep -E "o{,0}" #就是不匹配o
echo "fooooood" | grep -oE 'fo{,3}'
fooo


#例如,“fo{1,3}”将匹配“fooooood”中的前三个o ,“o{0,1}”等价于“o?”
echo "fooooood" | grep -oE 'fo{1,3}' #请注意在逗号和两个数之间不能有空格
fooo
echo "fooooooooooooood" | grep -oE 'o{1,3}' #匹配1~3次
ooo
ooo
ooo
ooo
oo


#正则表达式A[0-9]{3} 能够匹配字符"A"后面跟着正好3个数字字符的串,A123、A348等,但是不匹配A1234
echo "A342 A3333 A666 A7894 " | grep -oE 'A[0-9]{3}\ '
A342
A666

#正则表达式[0-9]{4,6} 匹配连续的任意4个、5个或者6个数字
echo "A342 A3333 A666 A7894 A12345 " | grep -oE 'A[0-9]{4,5}\>' #匹配词(word)的结束
A3333
A7894
A12345

贪婪模式和非贪婪模式

#例如,对于字符串“loooo”,“lo?”将匹配单个“lo”,而“o+”将匹配所有“o”。
echo "loooo" | grep -oE "lo+" #loooo
echo "loooo" | grep -oE "lo?" #lo
echo "loooo helloooo" | grep -oE "lo+?"
loooo
l
loooo

注意事项:

  • 在 {,} 数量限定符中里面’,’两边不能有空格


位置限定符
* \< 	匹配词(word)的开始(\<)
* \> 匹配词(word)的结束(\>)
* \b 匹配单词开头和结尾置(即正则表达式的“匹配”有两种概念,一种是匹配字符,一种是匹配位置,这里的\b就是匹配位置的)
* \B 匹配非单词开头和结尾位置

基础案例:

# \<  \>
例如正则表达式\<the\>能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"
注意:这个元字符不是所有的软件都支持的。
echo "plain 123 test" | grep -oE '\<pl' #pl
echo "plain 123 test" | grep -oE 'st\>' #st


# \b
例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”,或者匹配单词开头与结尾
[[email protected] ~]# echo "never verb" | grep -E 'er\b'
echo "computer is whoami iss" | grep -oE '\bis' #匹配头
is
is
echo "computer is whoami iss" | grep -oE 'is\b' #匹配尾
is
echo "computer is whoami iss" | grep -oE '\bis\b' #匹配头尾
is
 

# \B
例如,“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”,或者匹配非单词开头与结尾
echo "verb never" | grep -E 'er\B'
echo "computer is whoami issisis" | grep -E '\Bis' #非开头位置字符is
echo "computer is whoami issisis" | grep -E 'is\B' #非结尾位置字符is
echo "computer is whoami issisis" | grep -E '\Bis\B' #非开头结尾位置字符is

WeiyiGeek.非开头和结尾

匹配位置常用正则表达式:
WeiyiGeek.匹配位置


元字符
# 数字与非数字 类匹配
\d 匹配一个数字字符
\D 匹配一个非数字字符

# 字母与非字母 类匹配,类似但不等价于“[A-Za-z0-9_]”,这里的"单词"字符使用Unicode字符集
\w 匹配包括下划线的任何单词字符
\W 匹配任何非单词字符

\f 匹配一个换页符 : 00001100 14 12 0C FF (NP form feed, new page) 换页键
\n 匹配一个换行符 :00001010 12 10 0A LF (NL line feed, new line) 换行键
\r 匹配一个回车符 :00001101 15 13 0D CR (carriage return) 回车键
\s 匹配任何不可见字符,包括空格、制表符、换页符等价于[ \f\n\r\t\v]。

\S 匹配任何可见字符 等价于[^ \f\n\r\t\v]

\t 匹配一个水平制表符 : 00001001 11 9 9 HT (horizontal tab) 水平制表符
\v 匹配一个垂直制表符 : 00001011 13 11 0B VT (vertical tab) 垂直制表符
\cx 匹配由x指明的控制字符


特殊元字符

描述:POSIX类是一个特殊的元字符类集

#grep 工具预定义(#define) 采用形式 [[:特殊符号:]]	

[:alnum:] 代表英文大小写字母及数字
[:alpha:] 代表英文大小写字母
[:lower:] 代表小写字母
[:upper:] 代表大写字母
[:digit:] 代表数字(Dec默认十进制)
[:xdigit:] 代表16 进位的数字类型
[:punct:] 代表标点符号
[:graph:] 代表空白字符以外的其他
[:blank:] 代表空格和 tab 键
[:print:] 可以被打印出来的任何字符
[:cntrl:] 键盘上的控制按键,如 CR,LF,TAB,DEL
[:space:] 任何会产生空白的字符如空格,tab,CR 等

任意建立一个文本并编辑或者以原有的文本做实验皆可
基础示例:

# cat > regular_express.txt<<END
"Open Source" is a good mechanism to develop programs.
apple is my favorite food.Football game is not use feet only.
this dress doesn't fit me.However, this dress is about $ 3183 dollars.
GNU is free air not free beer.Her hair is very beauty.I can't finish the test.
Oh! The soup taste good.motorcycle is cheap than car.This window is clear.
the symbol '*' is represented as start.Oh!My god!The gd software is a library for drafting programs.
You are the best is mean you are the no. 1.
The world <Happy> is the same with "glad".
I like dog.google is the best tools for search keyword.
goooooogle yes!g
o! go! Let's go.
# I am VBird
END

示例:

#查找小写字母:
grep -n -E '[[:lower:]]' regular_express.txt

#查找数字
grep -n -E '[[:digit:]]' regular_express.txt

#匹配数字和字母
echo "verb123never" | grep -oE '[[:digit:]]'
123
echo "verb123never" | grep -oE '[[:alpha:]]'
verb123never

WeiyiGeek.lower


特殊符号之分组

分组:正则表达式中的分组又称为子表达式,就是把一个正则表达式的全部或部分当做一个整体进行处理,分成一个或多个组
其中分组是使用“()”表示的,进行分组之后“()”里面的内容就会被当成一个整体来处理,将正则表达式得一部分用括号括起来组成一个单元,可以对整个单元使用数量限定符;

分组常用:

* \( \)	将 \( 和 \) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用。

分组可以分为捕获组非捕获组.

  • 捕获组:可以通过从左到右通过其括号对其进行编号,至于后向引用其实就是对匹配组的一种快捷指令
    • 用法在0x02会介绍需要注意的是组零永远代表的是整个正则式,通过这样命名保存了匹配分组的匹配的序列,捕获的子序列就可以通过后向引用在表达式中使用了,也可以从匹配器中检索
      非捕获组:以 (?) 开头的组是纯的非捕获组,它不捕获文本 ,也不针对组合计进行计数
    • 就是说如果小括号中以?号开头,那么这个分组就不会捕获文本,当然也不会有组的编号,因此也不存在Back 引用

基础案例:

#echo "192.168.200.255" | grep -E '^([0-9]{1,3}\.){3}[0-9]{1,3}$
192.168.200.255


向后引用
描述:当一个正则表达式被分组后,每个组将会自动的分配一个组号用于代表该组的表达式,
其中,组号的编制规则为:从左到右、以分组的左括号“(”为标志,第一个分组的组号为1,第二个分组的组号为2,以此类推。

基础示例:

#匹配“javajava”
1)仅仅使用分组实现:(Java)(java)
2)使用后向引用的方法:(java)\1

echo "javajava" | grep -oE "(java)" #对比两种情况
java
java
echo "javajava" | grep -oE "(java)\1"
javajava


#对比(\w)\1和(\w)(\w)的区别

WeiyiGeek.区别

#\num  对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符
echo "jaava javee" | grep -oE "(.)\1"
aa
ee

#\nm
如果\nm之前至少有nm个获得子表达式,则nm为向后引用
如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用
echo qwqwqwqwjq | grep -oE "(qw)\1jq" #注意特殊点
qwqwjq

应用实例

过滤空白和注释行

#特殊应用实例:查看/etc/ssh/sshd_config 文档
'^$' : 过滤掉空白行
'^#' :过滤掉注释行(以#号开头)

#示例:-n显示上一次的行数,-v表示反向匹配显示
grep -nv '^$' /etc/ssh/sshd_config | grep -nv '^#'
1:1:# $OpenBSD: sshd_config,v 1.100 2016/08/15 12:32:04 naddy Exp $
2:3:# This is the sshd server system-wide configuration file. See
3:4:# sshd_config(5) for more information.
4:6:# This sshd was compiled with PATH=/usr/local/bin:/usr/bin