Shell正则表达式实战从入门到精通
Shell正则表达式处理文本,涵盖基本正则(BRE)、扩展正则(ERE)及Perl兼容正则(PCRE)。结合grep、sed、awk工具可高效查找、替换和提取文本。POSIX字符类增强兼容性,需注意元字符转义、单引号使用及贪婪匹配问题,助力实战应用。
正则表达式(Regular Expression,简称 Regex)在 Shell 编程中堪称文本处理的利器,配合 grep、sed、awk 等工具,可以高效地查找、替换和提取文本。本教程从基础概念逐步过渡到进阶技巧,每一部分都配有可直接运行的示例,帮助读者快速上手并真正掌握 Shell 正则的实战应用。

一、正则表达式概述与 Shell 工具支持
首先需要明确,Shell 中存在三种正则规范,不同工具对其支持程度不同。混淆这些规范容易导致错误。
1. 正则表达式的类型
| 类型 | 说明 | 支持工具 |
|---|---|---|
| 基本正则(BRE) | 基础元字符,部分元字符需转义 | grep(默认)、sed(默认) |
| 扩展正则(ERE) | 更多元字符,无需额外转义 | grep -E、sed -E、awk |
| Perl 兼容正则(PCRE) | 功能最强,支持非贪婪匹配等高级特性 | grep -P |
2. 核心工具速览
grep:文本查找(Global Regular Expression Print)sed:文本替换、编辑(Stream Editor)awk:文本处理、数据分析(Aho-Weinberger-Kernighan)
二、基本正则表达式(BRE)
基本正则是最早出现的规范,也是很多工具的默认模式。其规则相对简单,但需注意——像 +、、{} 这类在其他语言中常见的元字符,在 BRE 中必须转义才能生效,否则会被视为普通字符。
1. 基础元字符
| 元字符 | 功能说明 | 示例 | 示例解释 |
|---|---|---|---|
^ | 匹配行首 | grep '^root' /etc/passwd | 匹配以root开头的行 |
$ | 匹配行尾 | grep 'bash$' /etc/passwd | 匹配以bash结尾的行 |
. | 匹配任意单个字符(除换行) | grep 'r..t' /etc/passwd | 匹配r和t之间有 2 个任意字符的行(如root、rbtx) |
* | 匹配前一个字符 0 次或多次 | grep 'ro*t' /etc/passwd | 匹配rt、rot、root、rooot等 |
[] | 匹配括号内任意单个字符 | grep 'r[ao]t' file.txt | 匹配rat或rot |
[^] | 匹配不在括号内的任意字符 | grep 'r[^ao]t' file.txt | 匹配rbt、rct等,但不匹配rat、rot |
\ | 转义元字符,使其失去特殊含义 | grep 'www\.example\.com' file.txt | 匹配字面量www.example.com(.被转义) |
2. 范围匹配与重复(BRE 需转义)
在 BRE 中,下面这些元字符前面必须加上反斜杠 \ 才能实现所需功能:
| 元字符(转义后) | 功能说明 | 示例 | 说明 |
|---|---|---|---|
\{n\} | 匹配前一个字符恰好n次 | grep 'ro\{2\}t' /etc/passwd | 匹配root(o恰好 2 次) |
\{n,\} | 匹配前一个字符至少n次 | grep 'ro\{2,\}t' file.txt | 匹配root、rooot等 |
{n,m}(原文为 \{n,m\}) | 匹配前一个字符n到m次 | grep 'ro\{1,3\}t' file.txt | 匹配rot、root、rooot |
三、扩展正则表达式(ERE)
如果觉得 BRE 的转义规则较为繁琐,可以使用扩展正则。通过 grep -E、sed -E 或直接在 awk 中编写,这些元字符无需转义,表达式可读性更高。
1. 扩展元字符
| 元字符 | 功能说明 | 示例(用grep -E) | 说明 |
|---|---|---|---|
+ | 匹配前一个字符 1 次或多次 | grep -E 'ro+t' file.txt | 匹配rot、root、rooot(不匹配rt) |
| 匹配前一个字符 0 次或 1 次 | grep -E 'rot' file.txt | 匹配rt或rot |
| | 或逻辑,匹配左右任意一个 | grep -E 'root|admin' /etc/passwd | 匹配包含root或admin的行 |
() | 分组,将多个字符视为整体 | grep -E '(root)+' file.txt | 匹配root、rootroot等 |
{n} | 匹配前一个字符恰好n次(无需转义) | grep -E 'ro{2}t' file.txt | 匹配root |
{n,} | 匹配前一个字符至少n次 | grep -E 'ro{2,}t' file.txt | 匹配root、rooot等 |
{n,m} | 匹配前一个字符n到m次 | grep -E 'ro{1,3}t' file.txt | 匹配rot、root、rooot |
四、POSIX 字符类
在非英文环境中直接使用 [0-9] 或 [a-z] 可能产生意外匹配(例如中文系统或 UTF-8 编码下范围可能扩大)。Shell 提供了 POSIX 字符类,以 [:...:] 形式写在 [] 中,兼容性更好。
| 字符类 | 说明 | 等价于(ASCII) |
|---|---|---|
[:alnum:] | 字母和数字 | [a-zA-Z0-9] |
[:alpha:] | 字母 | [a-zA-Z] |
[:digit:] | 数字 | [0-9] |
[:lower:] | 小写字母 | [a-z] |
[:upper:] | 大写字母 | [A-Z] |
[:space:] | 空白字符(空格、制表符等) | [ \t\n\r\f\v] |
[:punct:] | 标点符号 | [!"#$%&'()*+,-./:;<=>@[\]^_`{|}~] |
示例:匹配包含数字的行
grep '[[:digit:]]' file.txt
五、实际工具应用(超详细示例)
理论结合实践,以下围绕 grep、sed、awk 三个核心工具,给出日常最常用的场景和写法。
1. grep:文本查找
基础用法
# 查找包含"error"的行(忽略大小写) grep -i 'error' log.txt # 查找不包含"debug"的行 grep -v 'debug' log.txt # 显示匹配行的行号 grep -n 'root' /etc/passwd # 递归查找当前目录下所有文件中的"TODO" grep -r 'TODO' .
进阶:结合扩展正则
# 查找以"202"开头的年份(如2020、2021...2029)
grep -E '^202[0-9]' dates.txt
# 查找邮箱(简化版)
grep -E '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' emails.txt
2. sed:文本替换与编辑
sed 默认使用 BRE,加 -E 启用 ERE。注意 sed -i 直接修改原文件,若无备份习惯,建议先输出到新文件确认无误后再操作。
基础替换
# 将文件中所有"apple"替换为"orange"(直接修改文件:加-i) sed 's/apple/orange/g' file.txt > new_file.txt sed -i 's/apple/orange/g' file.txt # 直接修改原文件 # 只替换第2次出现的"apple" sed 's/apple/orange/2' file.txt
进阶:结合正则与分组
# 将"root:x:0:0:root:/root:/bin/bash"改为"User: root, Shell: /bin/bash" # 分组1匹配root,分组2匹配/bin/bash sed -E 's/^([a-z]+):.*:([^:]+)$/User: \1, Shell: \2/' /etc/passwd # 删除空行 sed '/^$/d' file.txt # 删除以#开头的注释行 sed '/^#/d' file.txt
3. awk:文本处理与数据分析
awk 默认使用 ERE,处理结构化文本(例如日志、CSV、passwd 文件)时非常高效。
基础用法
# 打印/etc/passwd的第1列(用户名)和第7列(Shell)
awk -F: '{print $1, $7}' /etc/passwd
# 打印第3列(UID)大于1000的行
awk -F: '$3 > 1000' /etc/passwd
进阶:结合正则
# 打印以"r"开头的用户名及其Shell
awk -F: '$1 ~ /^r/ {print $1, $7}' /etc/passwd
# 统计日志中"error"出现的次数
awk '/error/ {count++} END {print "Error count:", count}' log.txt
# 提取IP地址(简化版)
awk '/([0-9]{1,3}\.){3}[0-9]{1,3}/ {print $0}' access.log
六、常见实用正则示例
以下匹配模式在实际工作中经常用到,可直接参考使用。
1. 匹配手机号(中国大陆)
grep -E '^1[3-9][0-9]{9}$' phones.txt
2. 匹配 IP 地址(简化版,不验证合法性)
grep -E '([0-9]{1,3}\.){3}[0-9]{1,3}' ips.txt
3. 匹配日期(YYYY-MM-DD)
grep -E '^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$' dates.txt
4. 提取 HTML 中的链接(简化版)
grep -Eo 'href="[^"]+"' index.html | sed -E 's/href="([^"]+)"/\1/'
七、注意事项与避坑指南
最后列举几个容易出错的地方,掌握后可节省调试时间。
- 转义问题:
- BRE 中
+、、|、()、{}需转义,ERE 中无需转义。编写前请确认当前工具使用的模式。 - 在 Shell 脚本中使用正则时,注意单引号
'和双引号"的区别:单引号内的元字符不会被 Shell 解析,建议使用单引号包裹正则。
- BRE 中
- 贪婪匹配 vs 非贪婪匹配:
- 默认采用贪婪匹配(尽可能多匹配),例如
a.*b匹配aabab中的整个字符串。 - 非贪婪匹配需使用 PCRE(
grep -P),例如a.*b匹配aab。
- 默认采用贪婪匹配(尽可能多匹配),例如
- 字符集问题:
- 处理中文时,需确保终端和文件编码一致(如 UTF-8),推荐使用 POSIX 字符类(例如
[:alpha:]可匹配中文字母)。
- 处理中文时,需确保终端和文件编码一致(如 UTF-8),推荐使用 POSIX 字符类(例如
