Shell 正则表达式完全指南

Shell 正则表达式完全指南

正则表达式(Regular Expression,简称 Regex)是一种用于匹配、查找和替换文本的模式语言。在 Shell 环境中,正则表达式被广泛应用于 grep、sed、awk、find 等工具,是文本处理和自动化脚本编写的核心技能。本文档将从基础到进阶,全面讲解 Shell 正则表达式的语法、流派差异及实战应用。

一、Shell 正则表达式的两大流派

Shell 工具对正则表达式的支持分为 基础正则表达式(Basic Regular Expression,BRE) 和 扩展正则表达式(Extended Regular Expression,ERE) 两大流派,核心区别在于特殊字符是否需要转义,不同工具对流派的支持不同,需重点区分。

1.1 流派核心差异对比

特性 基础正则表达式(BRE) 扩展正则表达式(ERE)
支持工具 grep(默认)、sed(默认)、vi grep -E/egrep、sed -E、awk
特殊字符转义要求 ?、+、{}、()、` ` 需转义
核心场景 简单匹配(如固定字符串、单字符通配) 复杂匹配(如重复次数、分支、分组)

1.2 工具与流派对应关系

工具 默认流派 启用 ERE 的方式 说明
grep BRE grep -E 或 egrep egrep 是 grep -E 的别名(部分系统)
sed BRE sed -E(GNU sed) BSD sed(如 macOS)需用 sed -E
awk ERE 无需额外参数 原生支持扩展语法,无需转义特殊字符
find BRE(有限支持) 无直接 ERE 选项 仅支持基础元字符,复杂匹配需结合 grep

二、基础正则表达式(BRE)语法

BRE 是 Shell 工具的默认正则流派,语法简洁,适用于简单文本匹配。以下是 BRE 的核心语法,按功能分类说明。

2.1 锚定符:定位匹配位置

锚定符不匹配具体字符,仅限定匹配内容在文本中的位置,是精准匹配的关键。

符号 名称 功能说明 示例
^ 行首锚定 匹配以指定内容开头的行(仅匹配行首第一个字符后的数据) grep '^root' /etc/passwd → 匹配所有以 root 开头的行
$ 行尾锚定 匹配以指定内容结尾的行(仅匹配行尾最后一个字符前的数据) grep 'bash$' /etc/passwd → 匹配所有以 bash 结尾的行
^$ 空行匹配 匹配无任何字符的空行(^ 行首 + $ 行尾,中间无内容) grep '^$' test.txt → 统计 test.txt 中的空行数
\b 单词边界 匹配单词的边界(单词指由字母、数字、下划线组成的连续字符) grep '\bhello\b' test.txt → 仅匹配独立的单词 hello(不匹配 helloworld)
\B 非单词边界 匹配非单词边界(与 \b 相反) grep '\Bhello\B' test.txt → 匹配 ohellow 中的 hello

2.2 字符匹配符:匹配单个字符

字符匹配符用于匹配单个字符,可灵活指定允许的字符范围或类型。

符号 名称 功能说明 示例
. 任意字符 匹配除换行符(\n)外的任意一个字符 grep 'h.llo' test.txt → 匹配 hello、hallo、hxllo 等
[] 字符集合 匹配集合中的任意一个字符 (集合内字符无序,支持 - 表示范围) grep 'h[aei]llo' test.txt → 仅匹配 hello、hallo、hillo
[^] 否定字符集 匹配不在集合中的任意一个字符 (^ 在 [] 内表示否定) grep 'h[^aei]llo' test.txt → 匹配 hxllo、hllo(不匹配 hello)
[a-z] 小写字母集 匹配任意一个小写英文字母(a 到 z,需按 ASCII 顺序) grep '[a-z]' test.txt → 匹配包含小写字母的行
[A-Z] 大写字母集 匹配任意一个大写英文字母 grep '[A-Z]' test.txt → 匹配包含大写字母的行
[0-9] 数字集 匹配任意一个数字(等价于 [0123456789]) grep '[0-9]' test.txt → 匹配包含数字的行
[a-zA-Z0-9_] 单词字符集 匹配任意一个字母、数字或下划线(等价于 \w,但 BRE 中 \w 兼容性较差) grep '[a-zA-Z0-9_]' test.txt → 匹配包含单词字符的行

2.3 重复匹配符:匹配多个连续字符

重复匹配符用于指定前一个字符或子表达式的重复次数 ,是简化多字符匹配的核心。注意:BRE 中 ?、+、{} 需转义。

符号 名称 功能说明 示例
* 零次或多次 匹配前一个字符0次或任意多次(尽可能多匹配,贪婪模式) grep 'ho*' test.txt → 匹配 h、ho、hoo、hooo 等
\? 零次或一次 匹配前一个字符0次或1次(可选字符) grep 'colou\?r' test.txt → 匹配 color(美式)和 colour(英式)
\+ 一次或多次 匹配前一个字符1次或任意多次(至少1次) grep 'a\+' test.txt → 匹配 a、aa、aaa(不匹配空)
\{n\} 恰好n次 匹配前一个字符恰好n次 grep 'a\{3\}' test.txt → 仅匹配 aaa(3个连续 a)
\{n,\} 至少n次 匹配前一个字符至少n次 grep 'a\{2,\}' test.txt → 匹配 aa、aaa、aaaa 等
\{n,m\} n到m次 匹配前一个字符n次到m次(包含n和m) grep 'a\{2,4\}' test.txt → 匹配 aa、aaa、aaaa(不匹配 a 或 aaaaa)

2.4 分组与分支(BRE 有限支持)

BRE 对分组和分支的支持较弱,需通过转义实现,主要用于复杂逻辑匹配。

符号 名称 功能说明 示例
\( \) 分组 将括号内的内容视为一个整体(子表达式),用于重复或引用 grep '\(ab\)*' test.txt → 匹配 ab、abab、ababab 等
` ` 分支 匹配左侧或右侧的表达式(逻辑"或"),需结合分组使用

三、扩展正则表达式(ERE)语法

ERE 是 BRE 的扩展,简化了特殊字符的使用(无需转义),支持更复杂的匹配逻辑,是 grep -E、sed -E、awk 的默认语法。

3.1 ERE 与 BRE 的核心区别

ERE 本质是 BRE 的"语法糖",核心差异在于特殊字符无需转义,具体对比如下:

功能 BRE 语法(需转义) ERE 语法(无需转义) 示例(匹配 2-4 个 a)
零次或一次 \? ? BRE:grep 'a\?' test.txt ERE:grep -E 'a?' test.txt
一次或多次 \+ + BRE:grep 'a\+' test.txt ERE:grep -E 'a+' test.txt
重复次数 \{n,m\} {n,m} BRE:grep 'a\{2,4\}' test.txt ERE:grep -E 'a{2,4}' test.txt
分组 \( \) ( ) BRE:grep '\(ab\)*' test.txt ERE:grep -E '(ab)*' test.txt
分支 ` ` `

3.2 ERE 专属高级语法

ERE 除简化 BRE 语法外,无新增核心符号,但结合工具(如 awk)可实现更灵活的应用,例如:

  • 分组引用 :在 sed -E 或 awk 中,可通过 \1、\2 引用分组内容(\1 表示第一个分组)。
    示例:sed -E 's/(hello)/\1 world/' test.txt → 将 hello 替换为 hello world。

四、Shell 工具正则实战案例

掌握语法后,需结合工具场景灵活应用。以下是 grep、sed、awk 的高频实战案例。

4.1 grep:文本搜索工具

grep 是最常用的正则匹配工具,核心功能是"搜索符合模式的行",支持 BRE(默认)和 ERE(-E)。

需求描述 命令(BRE/ERE) 说明
搜索以 # 开头的注释行(忽略空行) grep '^#' /etc/httpd/conf/httpd.conf ^# 匹配行首的 #,排除非注释行
搜索包含 1-3 个数字的行 grep -E '[0-9]{1,3}' test.txt(ERE) grep '[0-9]\{1,3\}' test.txt(BRE) ERE 无需转义 {},更简洁
搜索包含 hello 或 world 的行 `grep -E 'hello world' test.txt(ERE)<br>grep 'hello|world' test.txt`(BRE)
搜索独立单词 test(不匹配 test123) grep '\btest\b' test.txt(BRE/ERE 通用) \b 是单词边界,确保 test 是独立单词

4.2 sed:文本替换工具

sed 是流编辑器,核心功能是"按模式修改文本",默认使用 BRE,-E 启用 ERE。

需求描述 命令(BRE/ERE) 说明
将所有 hello 替换为 HELLO sed 's/hello/HELLO/g' test.txt(BRE) s/旧/新/g 是替换格式,g 表示"全局替换"(默认仅替换每行第一个匹配)
将 color 或 colour 统一为 color sed -E 's/colou?r/color/g' test.txt(ERE) u? 匹配 u 0 次或 1 次,同时覆盖美式和英式拼写
删除所有空行 sed '/^$/d' test.txt(BRE) /^$/ 匹配空行,d 表示"删除该行"
在每行开头添加 [INFO] 前缀 sed 's/^/[INFO] /' test.txt(BRE) ^ 匹配行首,在开头插入 [INFO]

4.3 awk:文本分析工具

awk 是强大的文本分析工具,原生支持 ERE,核心功能是"按行处理文本字段"(默认以空格分割字段,$1 表示第一个字段,$0 表示整行)。

需求描述 命令(ERE) 说明
提取 /etc/passwd 中 UID 为 0 的用户(root 及等效用户) awk -F ':' '$3 == 0 {print $1}' /etc/passwd -F ':' 指定分隔符为 :,$3 是 UID 字段,匹配 UID=0 并打印用户名($1)
统计日志中包含 ERROR 的行数 awk '/ERROR/ {count++} END {print count}' log.txt /ERROR/ 用 ERE 匹配包含 ERROR 的行,count++ 计数,END 块输出结果
提取 URL 中的域名(如 https://www.baidu.com/path → www.baidu.com) awk -F '[/:]' '/^https:\/\// {print $4}' urls.txt -F '[/:]' 指定分隔符为 / 或 :,$4 对应域名字段

五、常见问题与注意事项

5.1 转义字符的坑

  • Shell 转义 vs 正则转义 :Shell 会先解析命令中的特殊字符(如 $、*、\),再将结果传递给正则工具。若需匹配正则中的特殊字符(如 $ 行尾锚定),需避免被 Shell 解析。

    解决方案:用单引号 包裹正则表达式(Shell 不解析单引号内的字符),例如 grep '^hello$' test.txt(正确匹配整行 hello),而非双引号(grep "^hello$" test.txt 可能被 Shell 干扰)。

  • 工具差异 :BSD sed(macOS)和 GNU sed(Linux)对转义的处理略有不同,例如 macOS 中 sed 需用 sed -E 启用 ERE,而 Linux 中 sed -r 与 sed -E 等价。

5.2 贪婪匹配与非贪婪匹配

Shell 正则默认是贪婪模式 (尽可能匹配最长的内容),例如 grep 'a.*b' test.txt 会匹配 a1b2b 中的 a1b2b(而非 a1b)。
注意 :Shell 正则(BRE/ERE)不支持非贪婪模式(如 .*?),若需非贪婪匹配,需使用 perl 或 grep -P(部分系统支持,启用 Perl 兼容正则),例如 grep -P 'a.*?b' test.txt。

5.3 空行与空白行的区别

  • 空行:无任何字符(包括空格、制表符),正则为 ^$。
  • 空白行:包含空格或制表符(\t),正则为 ^[[:space:]]*$([[:space:]] 匹配任意空白字符,* 表示零次或多次)。
    示例:grep '^[[:space:]]*$' test.txt → 匹配空行和空白行。

六、总结

Shell 正则表达式是文本处理的核心工具,需重点掌握:

  1. 流派区分 :BRE(默认,需转义特殊字符)和 ERE(-E 启用,无需转义)。
  2. 核心语法 :锚定符(^、$)、字符匹配符(.、[])、重复匹配符(*、{n,m})、分组与分支(()、|)。
  3. 工具实战 :grep 搜索、sed 替换、awk 字段分析,结合具体场景选择合适工具。

通过多练习文本处理场景(如日志分析、配置文件修改、数据提取),可快速掌握正则表达式的灵活应用。

正则表达式练习题

2. 基础正则元字符实战案例(基于grep工具)

案例1:*匹配前面一个字符0次或多次

操作文件 :a.txt(内容如下)

复制代码
lk
lok
look
loook
looooook
loooooaaak
looooooook
abbbbcd
abbbbcd666
ooooloooook
oooooolk
aoblck

执行命令及结果:

bash 复制代码
# 匹配"lo"后接0个或多个"o"再接"k"(即"lok""look""loook"等,排除"lk")
[root@rhel8 ~]# grep "loo*k" a.txt
lok
look
loook
looooook
looooooook
ooooloooook

# 匹配"l"后接0个或多个"o"再接"k"(包含"lk""lok""look"等)
[root@rhel8 ~]# grep "lo*k" a.txt
lk
lok
look
loook
looooook
looooooook
ooooloooook
oooooolk
案例2:.匹配除\n之外的任意一个字符

操作文件 :同案例1的a.txt

执行命令及结果:

bash 复制代码
# 匹配"lo"后接任意字符(0个或多个)再接"k"(包含中间有其他字符的情况,如"loooooaaak")
[root@rhel8 ~]# grep "lo.*k" a.txt 
lok
look
loook
looooook
loooooaaak
looooooook
ooooloooook

# 重复执行上述命令,结果一致
[root@rhel8 ~]# grep "lo.*k" a.txt 
lok
look
loook
looooook
loooooaaak
looooooook
ooooloooook

# 匹配"lo"后接1个任意字符再接"k"(仅"look"符合)
[root@rhel8 ~]# grep "lo.k" a.txt
look

# 匹配"l"后接2个任意字符再接"k"(仅"look"符合,"l"+"oo"+"k")
[root@rhel8 ~]# grep "l..k" a.txt
look
案例3:\{n\}、\{n,\}、\{n,m\}匹配字符重复次数

操作文件 :同案例1的a.txt

执行命令及结果:

bash 复制代码
# 匹配"lo"后接2个"o"再接"k"(即"look","o"恰好出现2次)
[root@rhel8 ~]# grep "lo\{2\}k" a.txt 
look

# 匹配"lo"后接3个"o"再接"k"(即"loook","o"恰好出现3次)
[root@rhel8 ~]# grep "lo\{3\}k" a.txt 
loook

# 匹配"lo"后接3个及以上"o"再接"k"("loook""looooook"等)
[root@rhel8 ~]# grep "lo\{3,\}k" a.txt 
loook
looooook
looooooook
ooooloooook

# 匹配"lo"后接3-5个"o"再接"k"(仅"loook""ooooloooook"符合)
[root@rhel8 ~]# grep "lo\{3,5\}k" a.txt 
loook
ooooloooook
案例4:^、$匹配字符串首尾位置

操作文件 :b.txt(内容如下,含空行)

复制代码
aa

abd
cdd
cdc
cdd

执行命令及结果:

bash 复制代码
# 匹配以"c"开头的字符串("cdd""cdc")
[root@rhel8 ~]# grep "^c" b.txt 
cdd
cdc
cdd

# 匹配以"d"结尾的字符串("abd""cdd")
[root@rhel8 ~]# grep "d$" b.txt 
abd
cdd
cdd

# 匹配空行(输出结果为空行,对应文件中的空行)
[root@rhel8 ~]# grep "^$" b.txt 

[root@rhel8 ~]# 
案例5:[list]、[^list]匹配指定/非指定字符

操作文件 :c.txt(内容如下)

复制代码
lok
lo12k
lo1k
loAk
loBk
look
loak
lodk
abcd
1234

执行命令及结果:

bash 复制代码
# 匹配"lo"后接字母(a-z、A-Z)或数字(0-9)再接"k"(排除"lok""lo12k")
[root@rhel8 ~]# grep "lo[a-zA-Z0-9]k" c.txt 
lo1k
loAk
loBk
look
loak
lodk

# 匹配"lo"后接"A""B""o"中的任意一个字符再接"k"("loAk""loBk""look")
[root@rhel8 ~]# grep "lo[ABo]k" c.txt 
loAk
loBk
look

# 匹配"lo"后接非字母(a-z、A-Z)的字符再接"k"(仅"lo1k"符合)
[root@rhel8 ~]# grep "lo[^a-zA-Z]k" c.txt 
lo1k

# 匹配包含非字母(a-z、A-Z)的字符串("lo12k""lo1k""1234")
[root@rhel8 ~]# grep "[^a-zA-Z]" c.txt 
lo12k
lo1k
1234

三、扩展正则

扩展正则是基础正则的补充,支持更多灵活的匹配逻辑,核心元字符及功能如下:

元字符 功能说明 示例
+ 匹配前面一个字符出现1次或多次(比*更严格,至少1次) lo+k(匹配lo后接1个或多个o再接k,如lok、look、loook)
? 匹配前面一个字符出现0次或1次(可选字符匹配) lo?k(匹配l后接0个或1个o再接k,即lk、lok)
() 将括号中的字符串作为一个整体(分组匹配) l(oo)+k(匹配l后接1个或多个"oo"整体再接k,如look、looook)
` ` 以"或"逻辑匹配多个字符串(分支匹配)
{} 为可重复的正则表达式指定重复次数(间隔匹配),功能同基础正则的\{n\}/\{n,\}/\{n,m\},但无需转义 lo{2}k(匹配lo后接2个o再接k)、lo{2,}k(匹配lo后接2个及以上o再接k)、lo{2,3}k(匹配lo后接2-3个o再接k)

2. 扩展正则元字符实战案例(基于egrep工具)

案例1:+匹配前面一个字符1次以上

操作文件 :同基础正则案例1的a.txt

执行命令及结果:

bash 复制代码
# 匹配"lo"后接1个或多个"o"再接"k"(排除"lk",包含"lok""look"等)
[root@rhel8 ~]# egrep "lo+k" a.txt 
lok
look
loook
looooook
looooooook
ooooloooook
案例2:?匹配前面一个字符0次或1次

操作文件 :同基础正则案例1的a.txt

执行命令及结果:

bash 复制代码
# 匹配"l"后接0个或1个"o"再接"k"(仅"lk""lok""oooooolk"符合)
[root@rhel8 ~]# egrep "lo?k" a.txt 
lk
lok
oooooolk
案例3:()将括号中的字符串作为整体匹配

操作文件 :同基础正则案例1的a.txt

执行命令及结果:

bash 复制代码
# 匹配"l"后接1个或多个"oo"整体再接"k"("look""looooook""looooooook",即"oo"重复1次、2次、3次)
[root@rhel8 ~]# egrep "l(oo)+k" a.txt 
look
looooook
looooooook
案例4:|以"或"逻辑匹配多个字符串

操作文件 :先向a.txt追加内容labk,再执行匹配

bash 复制代码
[root@rhel8 ~]# echo labk >> a.txt

执行命令及结果:

bash 复制代码
# 匹配"l"后接1个或多个"oo"整体,或1个或多个"ab"整体,再接"k"("look""looooook""looooooook""labk")
[root@rhel8 ~]# egrep "l(oo|ab)+k" a.txt 
look
looooook
looooooook
labk
案例5:{}指定字符重复次数

操作文件 :同基础正则案例1的a.txt(已追加labk)

执行命令及结果:

bash 复制代码
# 匹配"lo"后接3个"o"再接"k"(仅"loook"符合)
[root@rhel8 ~]# egrep "lo{3}k" a.txt 
loook

# 匹配"lo"后接3个及以上"o"再接"k"("loook""looooook""looooooook""ooooloooook")
[root@rhel8 ~]# egrep "lo{3,}k" a.txt 
loook
looooook
looooooook
ooooloooook

# 匹配"lo"后接3-5个"o"再接"k"(仅"loook""ooooloooook"符合)
[root@rhel8 ~]# egrep "lo{3,5}k" a.txt 
loook
ooooloooook

四、特殊的字符组

特殊字符组是基础/扩展正则中预设的"字符集合缩写",简化对特定类型字符的匹配,适用于所有支持正则的Linux工具,具体如下:

特殊字符组 描述
[[:alpha:]] 匹配任意字母字符(大写A-Z或小写a-z)
[[:alnum:]] 匹配任意字母数字字符(0-9、A-Z、a-z)
[[:blank:]] 匹配空格( )或Tab键(\t)
[[:digit:]] 匹配0-9之间的任意一个数字(等价于[0-9])
[[:lower:]] 匹配小写字母字符(a-z,等价于[a-z])
[[:print:]] 匹配任意可打印字符(包括字母、数字、标点、空格等,排除不可见字符如\n)
[[:punct:]] 匹配任意标点符号(如!、@、#、$、,、.等)
[[:space:]] 匹配任意空白字符(包括空格、Tab键、换行符\n、换页符\f、垂直制表符\v、回车符\r)
[[:upper:]] 匹配任意大写字母字符(A-Z,等价于[A-Z])
相关推荐
小白快快跑哦1 天前
python-字符串全解(六):正则表达式-逻辑运算或
python·正则表达式·字符串·逻辑运算
MoRanzhi12031 天前
106.正则问题(分治)
python·算法·正则表达式·蓝桥杯·递归·分治算法·字符串处理
临床数据科学和人工智能兴趣组2 天前
在R语言的生态系统中,字符串处理一直是一个重要且复杂的任务
开发语言·正则表达式·r语言·r语言-4.2.1
小白快快跑哦3 天前
python-字符串全解(六):正则表达式-前后相断言
开发语言·python·正则表达式·前后相断言
小白快快跑哦4 天前
python-字符串全解(六):正则表达式-修饰符
python·正则表达式·修饰符
11路没有终点5 天前
正则表达式(Regex)通用学习文档
正则表达式
小白快快跑哦5 天前
python-字符串全解(六):正则表达式-分组
python·mysql·正则表达式·分组
临床数据科学和人工智能兴趣组5 天前
正则表达式的基础语法的基本符号
开发语言·人工智能·python·正则表达式·r语言·r语言-4.2.1·workbudyy
只愿云淡风清6 天前
正则表达式学习笔记-01 正则表达式的来源
笔记·学习·正则表达式
半亩码田6 天前
C#转Python第4.8篇:正则表达式:Python re 模块 vs C# System.Text.RegularExpressions
python·正则表达式·c#