第 45 课:正则表达式详解
所属模块: Shell 脚本进阶
难度: 中级
标签: regex, pattern
1. 学习目标
完成本课学习后,你应该能够:
- 理解正则表达式的基本组成元素(字符、元字符、量词、锚定符等)。
- 掌握基础正则表达式与扩展正则表达式的关键区别。
- 能够使用
grep、sed等工具配合正则表达式进行文本搜索、过滤和替换。 - 能够编写并调试相对复杂的正则表达式模式,以匹配特定格式的数据。
2. 核心概念
正则表达式(Regular Expression,简称 regex)是一种强大的模式匹配“语言”,它用于描述、匹配一系列符合某个句法规则的字符串。在 Linux 系统管理中,它是我们处理日志、配置文件、输出流等的得力工具。
2.1 正则表达式的基本元素
- 普通字符: 如
a,b,1,#,匹配其自身。 - 元字符: 具有特殊含义的字符,如
.、*、?、[]、^、$、\等。要匹配元字符本身,需要用反斜杠\转义。 - 字符类
[]: 匹配方括号中的任意一个字符。如[abc]匹配a、b或c。[0-9]匹配任意数字,[a-zA-Z]匹配任意字母。 - 量词: 指定前面元素出现的次数。
*:零次或多次。+:一次或多次(扩展正则表达式,ERE)。?:零次或一次(ERE)。{n}:恰好 n 次。{n,}:至少 n 次。{n,m}:n 到 m 次。
- 锚定符: 用于匹配位置。
^:匹配行首。$:匹配行尾。\<:匹配单词开头(部分工具支持)。\>:匹配单词结尾(部分工具支持)。
- 特殊字符类: 提供了便捷的通用类别。
.:匹配除换行符外的任意单个字符。\d:匹配数字([0-9],部分工具如perl的正则支持,标准grep/sed通常不支持,需用[0-9])。\w:匹配单词字符([a-zA-Z0-9_])。\s:匹配空白字符(空格、制表符等)。
2.2 基础正则表达式 vs. 扩展正则表达式
这是 Linux 工具中一个关键的区别:
- 基础正则表达式 (BRE): 默认模式,例如在
grep和sed中。?,+,{,|,(,)这些字符需要转义(\?,\+,\{,\|,\(,\)) 才能发挥元字符作用。 - 扩展正则表达式 (ERE): 使用
-E选项(grep -E,egrep,sed -E)。上述字符直接作为元字符使用,不需要转义,语法更接近其他编程语言的正则表达式。
一个简单比喻: BRE 是“谨慎模式”,你需要明确说“我是个特殊符号”(加 \);ERE 是“默认模式”,这些符号天生就是特殊符号。
3. 代码示例
示例 1:使用 grep 匹配复杂模式
# 在文件 /etc/passwd 中,查找用户名以字母开头,后面跟着3个数字的行
# 使用 ERE (-E),避免转义 '+' 和 '{}'
grep -E '^[a-zA-Z][0-9]{3}' /etc/passwd
# 匹配包含 “error” 或 “warning” (不区分大小写) 的日志行
grep -iE 'error|warning' /var/log/syslog
示例 2:使用 sed 进行正则替换
# 将文本中的所有连续多个空格,替换为一个空格
# 注意:在 BRE 中,`+` 需要转义为 `\+`
echo “This is a test.” | sed ‘s/ \+/ /g’
# 提取并重新格式化日期 (假设格式为 YYYY-MM-DD)
# 使用捕获组和反向引用 (`\1`, `\2`, `\3`)
echo “Date: 2023-10-27” | sed -E ‘s/.*([0-9]{4})-([0-9]{2})-([0-9]{2}).*/\2\/\3\/\1/’
# 输出: Date: 10/27/2023
示例 3:一个完整的脚本片段 - 提取IP地址
#!/bin/bash
# 从日志文件中提取所有 IPv4 地址
LOG_FILE=“/var/log/auth.log”
# 一个经典的IPv4地址匹配模式 (ERE)
# 解释:\b 单词边界, ([0-9]{1,3}\.){3} 匹配三个数字和点, [0-9]{1,3} 匹配最后一段
IP_PATTERN=‘\b([0-9]{1,3}\.){3}[0-9]{1,3}\b’
grep -oE “$IP_PATTERN” “$LOG_FILE” | sort -u
# `grep -o` 只输出匹配到的部分,`sort -u` 去重排序
4. 实践练习
练习 1:基础匹配
编写一个 grep 命令,从 /etc/services 文件中找出所有以 # 注释开头、且注释内容中包含 tcp 这个词的行。
预期输出示例:
# Note: it is presently (2020) not possible to assign a service to both tcp and udp.
练习 2:数字与格式匹配
编写一个 grep 命令,使用扩展正则表达式,从输入文本中匹配所有符合“中国大陆手机号码”格式的数字(11位,以1开头,第二位是3-9)。测试输入可以是:
电话:13812345678,备用:15698765432,座机:010-88888888,无效:12345。
预期输出:
电话:13812345678,备用:15698765432
练习 3:综合替换
使用 sed 和正则表达式,将以下 Markdown 风格的链接文本转换为纯文本标题,并在后面用括号附上 URL。
输入:[点击这里](https://example.com) 访问网站。
期望输出:点击这里 (https://example.com) 访问网站。
提示: 使用捕获组 (...) 和反向引用。
5. 常见错误
- 忘记转义 BRE 中的特殊字符: 在
grep或sed(不使用-E) 中,想用+匹配一次或多次,却写成了a+而不是a\+,导致+被当作普通字符匹配。 - 混淆
.和\.:.匹配任意字符,但想匹配真正的点号时,需要转义grep ‘\.txt’ files,否则像atxt也会被匹配。 - 贪婪匹配: 默认的匹配是“贪婪”的,会尽可能多地匹配。例如
.*会匹配整行。需要“懒惰”匹配时(如.*?),需要工具支持(如grep -P启用 PCRE 或sed -E配合其他技巧),这是一个进阶话题。 - 在字符类
[]中误用元字符: 在[]内部,大多数元字符会失去特殊含义,只有]、\、^、-需要特殊处理(如^在开头表示否定)。写grep ‘[.*]’ file会匹配字面意义上的点、星号或任意字符,而不是你可能期望的“匹配点或星号”。 - 过度依赖
\d,\w等: 标准的grep和sed默认不支持这些速记符。请使用[0-9]、[a-zA-Z0-9_]等代替,或使用grep -P(Perl 正则表达式)。
6. 小结
- 正则表达式是文本处理的“瑞士军刀”,由普通字符和元字符组成。
- 关键区别:基础正则表达式 (BRE) 中
?+{|()需要转义,而扩展正则表达式 (ERE,grep -E) 中直接使用。 - 核心构造:使用 字符类
[]定义一组字符,量词*+?{}控制重复次数,锚定符^$定位行首行尾。 grep用于搜索匹配行,sed用于替换或转换,两者都深度依赖正则表达式。- 编写正则时,从简单开始,逐步复杂化,并使用
echo和grep快速测试模式。
练习编辑器
rust
Loading...