5.Shell脚本修炼手册---Linux正则表达式(Shell三剑客准备启动阶段)
Linux 正则表达式入门指南
文章目录
- Linux 正则表达式入门指南
- 什么是正则表达式?
- 正则表达式的分类
- 重要命令说明:grep -E 与 egrep
- 实验环境准备
- 一、普通字符匹配
- 操作步骤:
- 二、字符集:匹配指定范围的字符
- 2.1 基础字符集 `[...]`
- 操作步骤:
- 2.2 范围字符集 `[a-z]、[A-Z]、[0-9]`
- 操作步骤:
- 2.3 取反字符集 `[^...]`
- 操作步骤:
- 2.4 任意字符 `.`
- 操作步骤:
- 2.5 转义符 `\`
- 操作步骤:
- 2.6 逻辑或 `|`(扩展正则)
- 操作步骤:
- 2.7 预定义字符集
- 三、非打印字符
- 四、定位符:限定匹配的位置
- 4.1 行首 `^` 和行尾 `$`
- 操作步骤:
- 4.2 单词边界 `\b` 和非单词边界 `\B`
- 操作步骤:
- 4.3 单词边界的另一种表示:`\<` 和 `\>`
- 五、限定次数:控制元素出现的次数
- 5.1 `*`:匹配任意次(0 次或多次)
- 操作步骤:
- 5.2 `+`:至少出现 1 次(扩展正则)
- 操作步骤:
- 5.3 `?`:最多出现 1 次(扩展正则)
- 操作步骤:
- 5.4 `{n}`:精确出现 n 次(扩展正则)
- 操作步骤:
- 5.5 `{m,n}`:出现 m 到 n 次(扩展正则)
- 操作步骤:
- 5.6 `{m,}`:至少出现 m 次(扩展正则)
- 5.7 `{,n}`:最多出现 n 次(扩展正则)
- 5.8 分组 `()`:将多个字符视为整体(扩展正则)
- 操作步骤:
- 六、反向引用:重复匹配前面的分组
- 操作步骤:
- 七、实战案例:匹配 IPv4 地址
- 八、其他支持正则的工具
什么是正则表达式?
正则表达式就像一个 “搜索模板”(pattern),我们可以用这个模板在字符串中查找符合规则的内容。它由两部分组成:
- 普通字符:就是我们平时输入的字母(a-z、A-Z)、数字(0-9)、标点(如!、, 等)这些没有特殊含义的字符。
- 元字符:有特殊功能的字符(如.、*、+ 等),它们能帮我们实现 “模糊匹配”(比如匹配任意字符、重复次数等)。
正则表达式在很多工具(如 vim 编辑器、grep 搜索命令、less 分页工具)和编程语言(如 Perl、Python、C)中都能用。
正则表达式的分类
- 基础正则表达式:支持的元字符较少,部分特殊元字符(如 |、())需要加转义符 \ 才能使用。
- 扩展正则表达式:支持更多元字符,且无需转义就能直接使用 |、()、+、?、{} 等,用起来更方便。
重要命令说明:grep -E 与 egrep
grep -E
是 grep
命令的扩展模式,功能和 egrep
完全一样(现在系统里的 egrep 通常是 grep -E 的快捷方式)。它的核心作用是:
直接支持扩展正则表达式的语法,不用给 |、()、+、?、{} 这些元字符加转义符 \,写复杂规则时更简单。
扩展正则的核心元字符:
|
:“或” 逻辑,比如a|b
匹配 “a” 或者 “b”()
:分组,把多个字符当一个整体,比如(ab)+
匹配 “ab” 连续出现多次+
:前面的元素至少出现 1 次(如a+
匹配 “a”、“aa”、“aaa” 等)?
:前面的元素出现 0 次或 1 次(如a?
匹配空或 “a”){n,m}
:前面的元素出现 n 到 m 次(如a{2,3}
匹配 “aa” 或 “aaa”)
实验环境准备
先创建一个测试文件words
,用于后续所有实验:
# 用vim创建words文件并写入以下内容(也可以用echo追加)
[bq@shell ~]$ vim words
# 写入内容:
cat
category
acat
concatenate
dog
一、普通字符匹配
实验流程:验证普通字符的精确匹配规则(即正则中的字符会原样匹配字符串中的字符)。
操作步骤:
- 确认
words
文件中包含 “cat” 相关的字符串; - 用
grep
命令搜索 “cat”,观察哪些行会被匹配。
# 从words文件中筛选包含"cat"的行
# 原理:grep默认使用基础正则,这里"cat"是普通字符,会精确匹配字符串中连续的"c-a-t"
[bq@shell ~]$ cat words | grep 'cat'
cat # 完全是"cat",匹配
category # 包含"cat"(前三个字符),匹配
acat # 包含"cat"(后三个字符),匹配
concatenate # 中间包含"cat",匹配
二、字符集:匹配指定范围的字符
字符集用[]
表示,用于匹配括号内的任意一个字符。
2.1 基础字符集 [...]
实验流程:验证[ab]
匹配 “a” 或 “b”,观察包含 “c+a+t” 或 “c+b+t” 的行是否被匹配。
操作步骤:
- 向
words
文件添加含 “cbt” 和 “c1t” 的行(用于对比); - 用
grep 'c[ab]t'
搜索,检查结果是否符合预期。
# 向words文件追加两行测试数据
[bq@shell ~]$ echo cbt >> words # 新增"cbt"(c-b-t)
[bq@shell ~]$ echo c1t >> words # 新增"c1t"(c-1-t)# 搜索"c后面跟a或b,再跟t"的字符串
# [ab]表示匹配"a"或"b",所以"cat"(c-a-t)和"cbt"(c-b-t)会被匹配
[bq@shell ~]$ cat words | grep 'c[ab]t'
cat # c-a-t,匹配[ab]中的a
category # 包含cat(c-a-t),匹配
acat # 包含cat(c-a-t),匹配
concatenate # 包含cat(c-a-t),匹配
cbt # c-b-t,匹配[ab]中的b
# 注意:c1t中的1不在[ab]里,所以不匹配
2.2 范围字符集 [a-z]、[A-Z]、[0-9]
实验流程:验证范围字符集匹配连续范围的字符(如小写字母、大写字母、数字)。
操作步骤:
- 分别用
[a-z]
(小写字母)、[A-Z]
(大写字母)、[0-9]
(数字)作为匹配规则; - 观察不同规则下的匹配结果。
# 1. 匹配"c+小写字母+t"([a-z]表示所有小写字母)
[bq@shell ~]$ cat words | grep 'c[a-z]t'
cat # c-a-t(a是小写),匹配
category # 包含cat(a是小写),匹配
acat # 包含cat(a是小写),匹配
concatenate # 包含cat(a是小写),匹配
cbt # c-b-t(b是小写),匹配
# 说明:c1t(1是数字)、后续新增的cCt(C是大写)不匹配# 2. 向words添加"cCt"(c-大写C-t)用于测试大写字母
[bq@shell ~]$ echo cCt >> words# 匹配"c+大写字母+t"([A-Z]表示所有大写字母)
[bq@shell ~]$ cat words | grep 'c[A-Z]t'
cCt # c-C-t(C是大写),匹配# 3. 匹配"c+数字+t"([0-9]表示所有数字)
[bq@shell ~]$ cat words | grep 'c[0-9]t'
c1t # c-1-t(1是数字),匹配# 4. 组合范围:[a-z0-9]表示小写字母或数字
[bq@shell ~]$ cat words | grep 'c[a-z0-9]t'
cat # a是小写,匹配
category # 包含cat,匹配
acat # 包含cat,匹配
concatenate # 包含cat,匹配
cbt # b是小写,匹配
c1t # 1是数字,匹配# 5. 包含大写字母的组合:[a-zA-Z0-9]
[bq@shell ~]$ cat words | grep 'c[a-zA-Z0-9]t'
cat
category
acat
concatenate
cbt
c1t
cCt # C是大写,现在被包含,匹配# 6. 匹配特殊字符"-":需把-放在[]的第一个位置(否则会被当范围符)
[bq@shell ~]$ echo c-t >> words # 新增"c-t"(c-'-'-t)
[bq@shell ~]$ cat words | grep 'c[-a-zA-Z0-9]t'
# 结果包含所有之前的匹配项,新增c-t(因为-在[]开头,被视为普通字符)
2.3 取反字符集 [^...]
实验流程:验证[^...]
匹配除括号内字符外的所有字符。
操作步骤:
- 用
[^ab]
表示 “除 a 和 b 之外的字符”; - 观察哪些行符合 “c + 非 a 非 b 字符 + t” 的规则。
# 匹配"c+不是a也不是b的字符+t"
[bq@shell ~]$ cat words | grep 'c[^ab]t'
c1t # 中间是1(非a非b),匹配
cCt # 中间是C(非a非b),匹配
c-t # 中间是-(非a非b),匹配
# 说明:cat(a)、cbt(b)不匹配# 注意:^只有放在[]开头才是取反,放中间则是普通字符
[bq@shell ~]$ cat words | grep 'c[a^b]t'
# 这里[a^b]表示匹配a、^、b中的任意一个,所以cat(a)、cbt(b)会匹配
cat
category
acat
concatenate
cbt
2.4 任意字符 .
实验流程:验证.
匹配除换行符外的任意单个字符。
操作步骤:
- 用
c.t
作为规则(c + 任意字符 + t); - 检查所有 “c + 任意字符 + t” 的行是否被匹配。
# . 表示任意单个字符(除换行符),所以c.t匹配"c+任意字符+t"
[bq@shell ~]$ cat words | grep 'c.t'
cat # c-a-t(.匹配a)
category # 包含cat(.匹配a)
acat # 包含cat(.匹配a)
concatenate # 包含cat(.匹配a)
cbt # c-b-t(.匹配b)
c1t # c-1-t(.匹配1)
cCt # c-C-t(.匹配C)
c-t # c-'-t(.匹配-)
2.5 转义符 \
实验流程:验证\
能将元字符转为普通字符(如将.
转为普通点号)。
操作步骤:
- 向
words
添加含c.t
(c. t)的行; - 用
\
转义.
,搜索c\.t
,观察是否只匹配含c.t
的行。
# 新增一行包含"c.t"(c后面是点号,再跟t)
[bq@shell ~]$ echo c.t >> words# 用\转义.,让它成为普通点号,只匹配"c.t"
[bq@shell ~]$ cat words | grep 'c\.t'
c.t # 只有这行符合"c. t",匹配# 注意:给普通字符加\虽然能匹配,但不推荐(多余操作)
[bq@shell ~]$ cat words | grep 'c\at'
cat # 这里\a被当普通a处理,所以匹配,但没必要这么写
category
acat
concatenate
2.6 逻辑或 |
(扩展正则)
实验流程:验证|
在扩展正则中表示 “或” 逻辑,匹配多个规则中的任意一个。
操作步骤:
- 用
egrep
或grep -E
启用扩展正则; - 搜索
cat|dog
,观察是否匹配含 “cat” 或 “dog” 的行。
# egrep 等价于 grep -E,支持扩展正则的|(或)
# 匹配含"cat"或"dog"的行(注意|前后不能加空格)
[bq@shell ~]$ cat words | egrep 'cat|dog'
cat
category
acat
concatenate
dog # 包含dog,匹配# 用grep -E效果相同
[bq@shell ~]$ cat words | grep -E 'cat|dog'
# 结果和上面一致
2.7 预定义字符集
系统预定义了一些常用字符集,方便直接使用:
预定义字符集 | 含义(等价写法) |
---|---|
[[:digit:]] | 数字(0-9,等价 [0-9]) |
[[:xdigit:]] | 十六进制数字(0-9、a-f、A-F,等价 [0-9a-fA-F]) |
[[:lower:]] | 小写字母(等价 [a-z]) |
[[:upper:]] | 大写字母(等价 [A-Z]) |
[[:alpha:]] | 字母(大小写,等价 [A-Za-z]) |
[[:alnum:]] | 字母 + 数字(等价 [0-9A-Za-z]) |
[[:space:]] | 空白字符(空格、制表符、换行等) |
[[:punct:]] | 标点符号(如!、@、# 等) |
[[:print:]] | 可打印字符(字母、数字、标点) |
三、非打印字符
非打印字符是终端中看不到的字符(如换行、制表符),常用的有:
字符 | 含义 |
---|---|
\n | 换行符(一行结束的标志) |
\s | 任意空白字符(空格、制表符等,等价 [\t\n]) |
\S | 非空白字符(等价 [^ \t\n]) |
\w | 单词字符(字母、数字、下划线,等价 [A-Za-z0-9_]) |
\W | 非单词字符(等价 [^A-Za-z0-9_]) |
\t | 制表符(Tab 键输入的字符) |
四、定位符:限定匹配的位置
定位符不匹配具体字符,而是限定匹配的位置(如行首、行尾、单词边界)。
4.1 行首 ^
和行尾 $
实验流程:验证^
匹配行首,$
匹配行尾,^...$
匹配整行。
操作步骤:
- 用
^cat
匹配以 “cat” 开头的行; - 用
cat$
匹配以 “cat” 结尾的行; - 用
^cat$
匹配整行只有 “cat” 的行。
# 1. ^cat:匹配以"cat"开头的行
[bq@shell ~]$ cat words | grep '^cat'
cat # 行首是cat,匹配
category # 行首是cat(后接egory),匹配# 2. cat$:匹配以"cat"结尾的行
[bq@shell ~]$ cat words | grep 'cat$'
cat # 行尾是cat,匹配
acat # 行尾是cat(前接a),匹配# 3. ^cat$:整行只能是"cat"(开头和结尾都是cat,即只有cat)
[bq@shell ~]$ cat words | grep '^cat$'
cat # 只有这行符合# 实用案例1:过滤配置文件中的有效行(排除注释行和空行)
# ^# 匹配以#开头的注释行,^$匹配空行,-v取反(不显示这些行)
[bq@controller ~]$ cat /etc/profile | egrep -v '^#|^$'# 实用案例2:查找ansible配置文件中的section(以[开头的行)
[bq@controller ~]$ cat /etc/ansible/ansible.cfg | grep '^\['# 实用案例3:筛选8月19日14:01到14:06的日志
[bq@controller ~]$ sudo cat /var/log/messages | egrep '^Aug 19 14:0[1-6]'
4.2 单词边界 \b
和非单词边界 \B
实验流程:验证\b
匹配单词边界(单词和非单词的分隔处),\B
匹配非单词边界。
操作步骤:
- 向
words
添加 “hello cat”(cat 是独立单词); - 用
\bcat
匹配 “cat” 作为单词开头,cat\b
匹配 “cat” 作为单词结尾,\bcat\b
匹配独立的 “cat” 单词; - 用
\Bcat
匹配 “cat” 不是单词开头的情况。
# 新增一行"hello cat"(cat是独立单词,前后有空格)
[bq@shell ~]$ echo hello cat >> words# 1. \bcat:匹配"cat"作为单词的开头(左边是单词边界)
[bq@shell ~]$ cat words | grep '\bcat'
cat # cat是独立单词,左边是行首(单词边界)
category # cat在开头(左边是行首,单词边界)
hello cat # cat左边是空格(单词边界)# 2. cat\b:匹配"cat"作为单词的结尾(右边是单词边界)
[bq@shell ~]$ cat words | grep 'cat\b'
cat # cat右边是行尾(单词边界)
acat # cat右边是行尾(单词边界)
hello cat # cat右边是行尾(单词边界)# 3. \bcat\b:匹配独立的"cat"单词(左右都是单词边界)
[bq@shell ~]$ cat words | grep '\bcat\b'
cat # 独立单词
hello cat # 独立单词# 4. \Bcat:匹配"cat"左边不是单词边界(即cat是其他单词的一部分)
[bq@shell ~]$ cat words | grep '\Bcat'
acat # cat左边是a(非单词边界,因为a是单词字符)
concatenate # cat左边是其他字母(非单词边界)
4.3 单词边界的另一种表示:\<
和 \>
\<
等价于\b
(单词左边界)\>
等价于\b
(单词右边界)
# \<cat 等价于 \bcat
[bq@shell ~]$ cat words | grep '\<cat'
cat
category
hello cat# cat\> 等价于 cat\b
[bq@shell ~]$ cat words | grep 'cat\>'
cat
acat
hello cat
五、限定次数:控制元素出现的次数
通过元字符控制前一个元素(字符或分组)出现的次数。
5.1 *
:匹配任意次(0 次或多次)
实验流程:验证*
匹配前一个元素出现 0 次、1 次或多次。
操作步骤:
- 向
words
添加 “dg”(d+g,o 出现 0 次)和 “doog”(d+oo+g,o 出现 2 次); - 用
do*g
匹配 “d + 任意个 o+g”,观察结果。
# 新增测试数据:dg(o出现0次)、doog(o出现2次)
[bq@shell ~]$ echo dg >> words
[bq@shell ~]$ echo doog >> words# do*g:d后面跟0个或多个o,再跟g
[bq@shell ~]$ cat words | grep 'do*g'
dog # o出现1次,匹配
dg # o出现0次,匹配
doog # o出现2次,匹配
5.2 +
:至少出现 1 次(扩展正则)
实验流程:验证+
匹配前一个元素出现 1 次或多次(必须用 egrep/grep -E)。
操作步骤:
- 用
do+g
作为规则(d + 至少 1 个 o+g); - 观察结果是否排除 o 出现 0 次的 “dg”。
# do+g:d后面跟1个或多个o,再跟g(+是扩展正则,需用egrep)
[bq@shell ~]$ cat words | egrep 'do+g'
dog # o出现1次,匹配
doog # o出现2次,匹配
# 注意:dg(o出现0次)不匹配
5.3 ?
:最多出现 1 次(扩展正则)
实验流程:验证?
匹配前一个元素出现 0 次或 1 次。
操作步骤:
- 用
do?g
作为规则(d+0 个或 1 个 o+g); - 观察结果是否包含 o 出现 0 次(dg)和 1 次(dog),排除 o 出现 2 次(doog)。
# do?g:d后面跟0个或1个o,再跟g
[bq@shell ~]$ cat words | egrep 'do?g'
dog # o出现1次,匹配
dg # o出现0次,匹配
# 注意:doog(o出现2次)不匹配
5.4 {n}
:精确出现 n 次(扩展正则)
实验流程:验证{n}
匹配前一个元素精确出现 n 次。
操作步骤:
- 用
do{2}g
作为规则(d+2 个 o+g); - 观察是否只匹配 o 出现 2 次的 “doog”。
# do{2}g:d后面跟2个o,再跟g
[bq@shell ~]$ cat words | egrep 'do{2}g'
doog # o出现2次,匹配
5.5 {m,n}
:出现 m 到 n 次(扩展正则)
实验流程:验证{m,n}
匹配前一个元素出现 m 到 n 次。
操作步骤:
- 向
words
添加 “dooog”(o 出现 3 次)和 “doooog”(o 出现 4 次); - 用
do{2,3}g
作为规则(o 出现 2-3 次),观察结果。
# 新增测试数据:dooog(o=3次)、doooog(o=4次)
[bq@shell ~]$ echo dooog >> words
[bq@shell ~]$ echo doooog >> words# do{2,3}g:o出现2到3次
[bq@shell ~]$ cat words | egrep 'do{2,3}g'
doog # o=2次,匹配
dooog # o=3次,匹配
# 注意:doooog(o=4次)不匹配
5.6 {m,}
:至少出现 m 次(扩展正则)
# do{2,}g:o至少出现2次
[bq@shell ~]$ cat words | egrep 'do{2,}g'
doog # o=2次
dooog # o=3次
doooog # o=4次
# 以上都匹配
5.7 {,n}
:最多出现 n 次(扩展正则)
# do{,3}g:o最多出现3次(0-3次)
[bq@shell ~]$ cat words | egrep 'do{,3}g'
dog # o=1次
doog # o=2次
dg # o=0次
dooog # o=3次
# 以上都匹配(doooog的o=4次,不匹配)
5.8 分组 ()
:将多个字符视为整体(扩展正则)
实验流程:验证()
将多个字符视为一个整体,结合限定次数元字符使用。
操作步骤:
- 向
words
添加 “dogdog”(dog 重复 2 次)、“dogdogdog”(重复 3 次)、“dogdogdogdog”(重复 4 次); - 用
(dog){2,3}
匹配 “dog” 重复 2-3 次的行。
# 新增测试数据:dog重复2-4次
[bq@shell ~]$ echo dogdog >> words
[bq@shell ~]$ echo dogdogdog >> words
[bq@shell ~]$ echo dogdogdogdog >> words# (dog){2,3}:"dog"作为整体,重复2-3次
[bq@shell ~]$ cat words | egrep '(dog){2,3}'
dogdog # 重复2次,匹配
dogdogdog # 重复3次,匹配
dogdogdogdog # 重复4次(包含3次),也匹配
六、反向引用:重复匹配前面的分组
反向引用用于重复匹配前面用()
捕获的内容,格式为\N
(N 是分组编号,1 开始)。
实验流程:验证反向引用\1
匹配第一个分组的内容。
操作步骤:
- 用
(bq) (laoniu).*\1
匹配 “bq laoniu…bq”(前后都是 bq); - 用
\b([a-z]+) \1\b
匹配重复的单词(如 “of of”)。
# 案例1:匹配"bq laoniu...bq"(\1引用第一个分组(bq))
[bq@shell ~]$ echo 'bq laoniu laohu bq laoniu laohu' | egrep -o '(bq) (laoniu).*\1'
bq laoniu laohu bq # 前面是bq,中间是laoniu,后面又出现bq(\1)# 案例2:匹配连续重复的单词(如"of of"、"up up")
[bq@shell ~]$ echo 'Is is the cost of of gasoline going up up?' | egrep -o '\b([a-z]+) \1\b'
of of # 分组([a-z]+)匹配of,\1重复of
up up # 分组匹配up,\1重复up# 案例3:匹配重复2次以上的单词(如"of of of")
[root@server ~]# echo 'Is is the cost of of of gasoline going up up?' | egrep -o '(\b[a-z]+\b\s+)\1{1,}'
of of of # 分组匹配"of ",\1{1,}表示重复1次以上,即"of of of"
七、实战案例:匹配 IPv4 地址
需求:从以下内容中过滤出有效的 IPv4 地址(每个段 0-255,且不能以 0 开头除非是 0 本身):
0.0.0.0
1.1.1.1
11.11.11.111
111.111.111.111
999.9.9.9 # 无效(999>255)
01.1.1.1 # 无效(以0开头且非0)
10.0.0.0
0.1.1.1
266.1.1.1 # 无效(266>255)
248.1.1.1
256.1.1.1 # 无效(256>255)
有效 IPv4 正则表达式:
# 解析:每个段需满足0-255,且不以0开头(除非是0)
# \b确保是完整的IP(不被其他字符包含)
\b(([1-9][0-9]?)|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))(\.(([0-9])|([1-9][0-9])|(1[0-9]{2})|(2[0-4][0-9])|(25[0-5]))){3}\b# 简化版
grep -E '\b([1-9][0-9]?|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){2}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\b' 文件名
验证结果:
上述正则会匹配除999.9.9.9
、01.1.1.1
、266.1.1.1
、256.1.1.1
之外的所有行。
八、其他支持正则的工具
- vim 编辑器:在命令模式用
/正则
搜索,用:%s/原正则/替换内容/g
批量替换。 - less 工具:查看文件时按
/
输入正则搜索,按 n 跳转到下一个匹配。
通过以上内容,你可以逐步掌握 Linux 正则表达式的核心用法,从简单的字符匹配到复杂的规则筛选,多练习就可以熟练运用!