正则表达式入门到精通教程(Linux实操版)
一、为什么你必须学会正则表达式
想象一下,你刚接手一个服务器日志文件,老板让你找出所有来自 404 错误的 IP 地址。如果没有正则表达式,你可能需要手动翻阅成千上万行文本;而掌握正则表达式后,一条命令就能搞定。正则表达式就像一把文本处理的瑞士军刀,在 Linux 系统管理、日志分析、数据清洗等场景中不可或缺。本教程将带你从零基础开始,通过实际 Linux 命令示例,逐步掌握这门强大的技能。
1.1 正则表达式到底是什么
正则表达式(Regular Expression)是一种用于匹配文本模式的工具,它不是某个编程语言的专属特性,而是一种通用的文本处理规则。你可以把它理解为一种文本搜索语言,用特定的符号组合来描述你想要匹配的字符串特征。
在 Linux 系统中,几乎所有文本处理工具都支持正则表达式,比如 grep、sed、awk、vim 等。掌握正则表达式,就相当于掌握了 Linux 文本处理的“核心密码”。
二、基础语法:从简单匹配开始
2.1 普通字符匹配
最基本的正则表达式就是直接使用文本字符进行匹配。比如你想在文件中查找包含 "error" 的行,可以直接使用:
# 在 example.log 文件中查找包含 "error" 的行
grep "error" example.log# 示例输出(假设 example.log 内容):
# 2025-10-20 14:30:02 ERROR connection failed
# 2025-10-20 14:35:10 WARNING disk space low, error may occur
这里的 "error" 就是一个最简单的正则表达式,它会精确匹配包含这个字符串的所有行。
2.2.1特殊字符与转义
有些字符在正则表达式中具有特殊含义,比如 . * + ? | ( ) [ ] { } ^ $ \。如果需要匹配这些字符本身,必须使用反斜杠 \ 进行转义。
# 匹配包含 "file.txt" 的行(. 是特殊字符,需要转义)
grep "file\.txt" example.log# 错误示例(不转义会匹配 "fileAtxt" "file1txt" 等)
grep "file.txt" example.log # 这会匹配 "fileXtxt" 形式的字符串
2.2 核心匹配规则
2.2.1 匹配任意字符:.
点号 . 可以匹配除换行符外的任意单个字符。
# 创建测试文件
echo -e "cat\ncot\ncut\ncar\nc3t" > test.txt# 匹配 "c" 开头,"t" 结尾,中间任意一个字符的行
grep "c.t" test.txt# 输出结果:
# cat
# cot
# cut
# c3t
# 解释:. 匹配了 a、o、u、3 这些不同的字符
2.2.2匹配多个字符:*
星号 * 表示匹配前面的元素零次或多次。
# 匹配 "ca" 后面跟任意多个 "t" 的行
grep "cat*" test.txt# 输出结果:
# cat
# 解释:* 匹配了 "t" 一次;如果有 "ca" 或 "catt" 也会被匹配
2.2.3 匹配边界:^ 和 $
^ 匹配行的开始,$ 匹配行的结束。
# 匹配以 "c" 开头的行
grep "^c" test.txt# 匹配以 "t" 结尾的行
grep "t$" test.txt# 匹配空行(开始即结束,中间没有任何字符)
grep "^$" test.txt
2.2.4 字符集:[ ]
方括号 [] 用于指定一个字符集合,匹配其中任意一个字符。
# 匹配 "c" 开头,"t" 结尾,中间是 a、o 或 u 的行
grep "c[auo]t" test.txt# 输出结果:
# cat
# cot
# cut
# 解释:[auo] 只匹配 a、u、o 这三个字符# 匹配数字开头的行
grep "^[0-9]" data.log# 匹配非数字开头的行(^ 在 [] 内表示取反)
grep "^[^0-9]" data.log
字符集支持范围表示法,例如 [a-z] 匹配所有小写字母,[0-9] 匹配所有数字,[A-Za-z0-9] 匹配所有字母和数字。
# 匹配 "user" 后面跟 1-3 位数字的行
grep "user[0-9]" test.txt # 1 位数字
grep "user[0-9][0-9]" test.txt # 2 位数字
2.2.5 重复次数:{n,m}
花括号 {} 用于指定元素的重复次数。注意在基本正则表达式(BRE)中需要转义,如 \{n,m\}。
# 匹配 "a" 出现 2-3 次的行(使用 grep -E 启用扩展正则表达式)
echo -e "aa\naaa\naaaa\na" | grep -E "a{2,3}"# 输出结果:
# aa
# aaa
# 解释:{2,3} 表示匹配 2 到 3 个 "a"# 基本正则表达式版本(需要转义花括号)
echo -e "aa\naaa\naaaa\na" | grep "a\{2,3\}"
2.2.6 或逻辑:|
竖线 | 表示逻辑“或”,匹配两边任意一个表达式。在 grep 中需要使用 -E 参数启用扩展正则表达式。
# 匹配包含 "error" 或 "warning" 的行
grep -E "error|warning" system.log# 等价于基本正则表达式(需要转义 |)
grep "error\|warning" system.log
2.2.7 分组匹配:( )
圆括号 () 用于将多个元素组合为一个单元,可以与 * + ? | 等结合使用。在 grep 中需要 -E 参数。
# 匹配 "cat" 或 "dog" 开头的行
grep -E "^(cat|dog)" test.txt# 匹配 "re" 或 "un" 开头,后面跟 "do" 的行
grep -E "^(re|un)do" words.txt
三、Linux 三剑客实战
3.1 grep:文本搜索利器
grep 是最常用的文本搜索工具,支持基本正则表达式(默认)和扩展正则表达式(-E 参数)。
常用参数:
-
-i:忽略大小写
-
-v:反向匹配(不包含模式的行)
-
-n:显示行号
-
-r:递归搜索目录
-
-A N:显示匹配行及其后 N 行
-
-B N:显示匹配行及其前 N 行
-
-C N:显示匹配行及其前后 N 行
# 在 /var/log 目录下递归搜索包含 "error" 的日志文件
grep -rni "error" /var/log# 查找不包含 "debug" 的行并显示行号
grep -nv "debug" app.log# 显示匹配行及其前后 2 行上下文
grep -C 2 "critical" server.log
3.2 sed:流编辑器
sed 主要用于文本替换,支持正则表达式匹配和修改。
基本语法:sed 's/原模式/替换文本/选项' 文件名
# 创建测试文件
echo "Hello world! This is a test. Test is important." > sedtest.txt# 替换第一个 "test" 为 "example"(只替换每行第一个匹配)
sed 's/test/example/' sedtest.txt# 替换所有 "test"(忽略大小写)
sed 's/test/example/gi' sedtest.txt# 替换并直接修改文件(-i 参数,强烈建议先备份)
sed -i.bak 's/Hello/Hi/' sedtest.txt # 会生成 sedtest.txt.bak 备份# 使用正则表达式删除空行
sed '/^$/d' input.txt > output.txt# 替换以 "Error" 开头的行为空行
sed '/^Error/d' log.txt
3.3 awk:文本分析工具
awk 适合处理结构化文本,支持更复杂的正则表达式匹配和数据处理。
# 创建测试数据文件
echo -e "name,age,city\nAlice,25,NewYork\nBob,30,London\nCharlie,35,Paris" > data.csv# 使用逗号分隔,打印第一列和第三列(姓名和城市)
awk -F ',' '{print $1, $3}' data.csv# 匹配 age 大于 28 的行
awk -F ',' '$2 > 28 {print $0}' data.csv# 使用正则表达式匹配城市以 "N" 开头的行
awk -F ',' '$3 ~ /^N/ {print $1}' data.csv # ~ 表示匹配正则表达式# 统计包含 "error" 的行数
awk '/error/ {count++} END {print "Total errors:", count}' server.log
四、进阶技巧
4.1 贪婪匹配与非贪婪匹配
正则表达式默认是贪婪匹配(尽可能匹配最长的字符串),在扩展正则表达式中,可以使用 *? +? ?? {n}? 实现非贪婪匹配(尽可能匹配最短的字符串)。
# 创建测试文件
echo "<div>content1</div><div>content2</div>" > html.txt# 贪婪匹配(默认):会匹配从第一个 <div> 到最后一个 </div> 的所有内容
grep -Eo "<div>.*</div>" html.txt
# 输出:<div>content1</div><div>content2</div># 非贪婪匹配:只匹配第一个 <div>...</div>
grep -Eo "<div>.*?</div>" html.txt
# 输出:<div>content1</div>
4.2 零宽断言
零宽断言用于匹配某个位置前后的内容,而不包含该位置本身。常见的有:
-
(?=pattern):正向先行断言(后面必须跟着 pattern)
-
(?!pattern):负向先行断言(后面不能跟着 pattern)
-
(?<=pattern):正向后行断言(前面必须是 pattern)
-
(?<!pattern):负向后行断言(前面不能是 pattern)
注意:grep 不支持零宽断言,需要使用 egrep 或其他工具如 perl。
# 匹配 "price" 后面跟着数字的行(但不包含数字本身)
echo -e "price100\npriceabc\ncost200" | perl -ne '/price(?=\d)/ && print'
# 输出:price100# 匹配 "123" 前面不是 "abc" 的行
echo -e "abc123\ndef123\nx123" | perl -ne '/(?<!abc)123/ && print'
# 输出:def123
# x123
4.3 正则表达式的组合使用
实际应用中通常需要组合多种规则来完成复杂匹配任务。
# 匹配邮箱地址的正则表达式
# 规则:用户名@域名.后缀,用户名可包含字母、数字、点、下划线、连字符
grep -Eo '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}' emails.txt# 解释:
# [A-Za-z0-9._%+-]+ : 匹配用户名部分(至少一个字符)
# @ : 匹配 @ 符号
# [A-Za-z0-9.-]+ : 匹配域名主体
# \. : 匹配点号(域名和后缀之间的点)
# [A-Za-z]{2,} : 匹配后缀(至少 2 个字母,如 com, org, cn 等)
五、实战案例
案例 1:分析 Nginx 访问日志
假设我们有 Nginx 访问日志 access.log,格式如下:
192.168.1.1 - - [22/Oct/2025:10:00:01 +0800] "GET /index.html HTTP/1.1" 200 1234
10.0.0.5 - - [22/Oct/2025:10:00:05 +0800] "POST /api/login HTTP/1.1" 401 567
192.168.1.1 - - [22/Oct/2025:10:00:10 +0800] "GET /images/logo.png HTTP/1.1" 200 9876
任务 1:提取所有 404 错误的 IP 地址
# 匹配状态码为 404 的行,提取 IP 地址(第一个字段)
awk '$9 == 404 {print $1}' access.log# 统计每个 IP 出现的次数(排序并去重)
awk '$9 == 404 {print $1}' access.log | sort | uniq -c | sort -nr
任务 2:找出访问次数最多的前 10 个 IP
# 提取所有 IP 地址,统计次数并排序
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -10
案例 2:处理 CSV 数据
假设我们有一个不规范的 CSV 文件 data.csv,需要清洗数据:
name,age,email,phone
Alice,25,alice@example.com,123-456-7890
Bob,30,bob@test, (987) 654-3210
Charlie,35,,555.123.4567
任务:提取有效的邮箱和电话号码
# 提取有效的邮箱地址
grep -Eo '[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}' data.csv# 提取电话号码(支持多种格式)
grep -Eo '(\+?\d{1,3}[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}' data.csv# 使用 awk 处理整行数据,过滤掉邮箱为空的行
awk -F ',' '$3 != "" {print $1 "," $3}' data.csv
案例 3:批量重命名文件
假设目录中有多个图片文件,命名格式不统一:
image1.jpg
pic_2.png
photo-3.gif
img_04.bmp
任务:统一重命名为 img_01.jpg, img_02.png 格式
# 使用 rename 命令(需要先安装,不同系统版本语法可能不同)
# Ubuntu/Debian 版本
rename 's/^(image|pic_|photo-|img_)(\d+)\.(jpg|png|gif|bmp)$/img_0$2.$3/' *# 如果数字超过 10,使用更复杂的正则表达式
rename 's/^(image|pic_|photo-|img_)(\d+)\.(jpg|png|gif|bmp)$/sprintf("img_%02d.%s", $2, $3)/e' *
六、常见问题与注意事项
6.1 正则表达式引擎差异
不同工具使用的正则表达式引擎可能不同,主要分为:
-
基本正则表达式(BRE):grep 默认模式
-
扩展正则表达式(ERE):grep -E, egrep, awk
-
Perl 兼容正则表达式(PCRE):grep -P, perl
主要差异:BRE 中 { } ? | ( ) 需要转义,ERE 和 PCRE 不需要。
# BRE 语法(需要转义)
grep "a\{2,3\}" file.txt# ERE 语法(不需要转义)
grep -E "a{2,3}" file.txt
6.2 性能优化
处理大文件时,正则表达式的效率很重要:
-
避免过度复杂的表达式
-
尽量使用锚点 ^ 和 $ 减少不必要的匹配
-
使用更具体的字符集代替通配符 .
# 低效:无锚点,全文件扫描
grep "error" large.log# 高效:有锚点,只匹配行首
grep "^Error" large.log
6.3 测试与调试
编写复杂正则表达式时,建议先在小样本上测试:
-
使用在线工具如 regex101.com 验证表达式
-
在命令行中先使用 grep -o 查看实际匹配内容
-
逐步构建表达式,不要一次性写复杂模式
七、学习资源与进阶路径
7.1 推荐工具
-
在线测试工具:Regex101(支持多种引擎和实时解释)
-
Linux 工具:grep, sed, awk, perl, vim
-
文本编辑器插件:VS Code Regex Previewer, Sublime Text Regex Helper
7.2 进阶学习方向
-
PCRE 高级特性:学习 Perl 兼容正则表达式的高级功能
-
正则表达式优化:学习如何编写高效的正则表达式
-
编程语言集成:在 Python, JavaScript 等语言中使用正则表达式
-
语法分析:结合语法规则解析更复杂的文本结构
八、总结
正则表达式是文本处理的强大工具,掌握它可以极大提高你的工作效率。从简单的字符匹配到复杂的文本分析,正则表达式都能胜任。学习正则表达式的关键是理解基本语法,然后通过大量实践积累经验。
记住,即使是正则表达式专家也需要不断查阅文档和测试表达式。不要害怕犯错,正则表达式就是在不断试错中逐渐掌握的技能。现在就打开你的终端,尝试用今天学到的知识处理实际问题吧!
九、练习题
-
编写一个正则表达式,匹配所有以字母开头,后面跟字母、数字和下划线的字符串(变量名)。
-
使用 grep 命令找出系统日志中过去 24 小时内的所有错误信息。
-
使用 sed 命令将文件中所有的 tab 字符替换为 4 个空格。
-
使用 awk 统计 Apache 访问日志中每个 HTTP 状态码的出现次数。
-
编写一个正则表达式,匹配中国大陆的手机号码(11 位数字,以 1 开头)。
答案可以在网上搜索验证,或者使用 regex101.com 进行测试。