学会 正则表达式(Regular Expression,简称 regex) 是 Shell 编程里从"会写脚本"到"会处理数据"的关键一步。
它让你能轻松地筛选、匹配、提取文本信息,是日志分析、批量处理、数据清洗的利器。
下面我来系统讲解:
什么是正则、怎么在 Shell 里用、有哪些工具支持、常见模式与实战例子。
🧠 一、正则表达式是什么?
正则表达式是一种 文本匹配模式(pattern) 。
它描述了一类字符串的结构,用来进行:
- 搜索(search)
- 替换(replace)
- 验证(validate)
- 提取(extract)
在 Shell 中,常用的工具如:
grep
egrep
/grep -E
sed
awk
expr
都支持正则表达式。
⚙️ 二、Shell 中正则的主要使用场景
命令 | 用途 | 是否支持正则 |
---|---|---|
grep |
搜索文本匹配行 | ✅ |
sed |
查找并替换文本 | ✅ |
awk |
模式匹配与处理 | ✅ |
expr |
简单正则提取 | ✅ |
[[ =~ ]] |
Bash 内置模式匹配 | ✅(Bash 3+) |
📘 三、基本正则表达式符号(BRE)
符号 | 含义 | 示例 | 匹配 |
---|---|---|---|
. |
任意单个字符 | a.b |
a1b , acb |
* |
前一个字符重复 0 次或多次 | lo*l |
ll , lool , looooool |
^ |
匹配行首 | ^abc |
"abc..." 开头的行 |
$ |
匹配行尾 | abc$ |
以 "abc" 结尾的行 |
[] |
字符集匹配 | [0-9] |
任意数字 |
[^] |
取反匹配 | [^0-9] |
非数字字符 |
\{m,n\} |
匹配前项 m 到 n 次 | a\{2,4\} |
aa , aaa , aaaa |
🔎 四、扩展正则(ERE)------ grep -E
或 egrep
扩展正则支持更多符号:
符号 | 含义 | 示例 | 说明 |
---|---|---|---|
+ |
前项至少出现一次 | a+b 匹配 ab 、aab |
重复匹配 |
? |
前项可有可无 | colou?r 匹配 color 或 colour |
可选项 |
` | ` | 或关系 | `cat |
() |
分组 | (ab)+ 匹配 "ab"、"abab" |
捕获分组 |
{m,n} |
限定重复次数 | a{2,4} 匹配 "aa"、"aaa"、"aaaa" |
次数限定 |
🧩 五、grep
命令:最常用的正则工具
1️⃣ 基本用法
bash
grep "root" /etc/passwd
匹配包含 "root" 的行。
2️⃣ 使用正则匹配复杂模式
bash
grep -E "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$" users.txt
匹配邮箱地址。
3️⃣ 区分大小写 / 忽略大小写
bash
grep -i "hello" file.txt
4️⃣ 统计匹配行数
bash
grep -c "error" /var/log/syslog
5️⃣ 只显示匹配部分
bash
grep -oE "[0-9]+" file.txt
提取所有数字。
🧰 六、sed
命令:替换与编辑
sed
(stream editor)也是正则的好搭档。
示例 1:替换单词
bash
sed 's/apple/banana/g' fruits.txt
把所有 apple
替换成 banana
。
示例 2:只替换行首出现的单词
bash
sed 's/^apple/banana/'
示例 3:删除空行
bash
sed '/^$/d'
🧮 七、awk
与正则结合
awk
的语法是 pattern { action }
示例:
bash
awk '/error/ { print $1, $2 }' /var/log/syslog
匹配包含 "error" 的行,输出前两列。
示例:匹配以数字开头的行
bash
awk '/^[0-9]/ { print $0 }' data.txt
⚡ 八、在 Bash 中直接用正则([[ =~ ]]
)
Bash 3.0 之后支持直接在 if
中匹配正则:
bash
#!/bin/bash
read -p "输入邮箱:" email
if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "邮箱格式正确"
else
echo "邮箱格式错误"
fi
✅ 优点:无需
grep
、sed
等外部命令🚫 注意:
[[ =~ ]]
右侧的正则不要加引号,否则不会生效。
🔍 九、expr
也能用简单正则
bash
expr "hello123" : '.*\([0-9][0-9][0-9]\)'
输出:123
表示匹配并提取字符串中的最后三位数字。
📘 十、常见正则模式速查表
目标 | 正则表达式 | 示例匹配 |
---|---|---|
邮箱 | ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ |
abc@163.com |
手机号(中国) | ^1[3-9][0-9]{9}$ |
13812345678 |
IPv4 地址 | ^([0-9]{1,3}\.){3}[0-9]{1,3}$ |
192.168.0.1 |
日期(YYYY-MM-DD) | ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ |
2025-10-10 |
整数 | ^-?[0-9]+$ |
-42 , 123 |
浮点数 | ^-?[0-9]+\.[0-9]+$ |
3.14 , -0.25 |
🧠 十一、实战:批量提取 IP 地址
bash
#!/bin/bash
# 从日志文件中提取所有 IP
grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log | sort | uniq -c | sort -nr
功能:
- 用
grep
匹配所有 IP; uniq -c
统计出现次数;sort -nr
按次数排序。
✅ 十二、总结
工具 | 支持正则 | 用途 |
---|---|---|
grep |
✅ | 搜索匹配行 |
sed |
✅ | 查找/替换文本 |
awk |
✅ | 复杂文本处理 |
expr |
✅ | 简单提取 |
[[ =~ ]] |
✅ | Bash 原生匹配 |
sed + 正则表达式
这部分内容能让人在日志分析、配置批处理、数据清洗中如虎添翼。
接下来我将从基础到高级讲解,包括分组、反向引用、条件替换、行匹配与多行操作。
🧠 一、sed
是什么?
sed
(stream editor )是一个流编辑器,用于:
- 查找(Search)
- 替换(Substitute)
- 删除(Delete)
- 插入(Insert)
- 提取(Print)
它一次读一行,对每一行执行给定的命令(如替换或删除)。
最常用命令格式:
bash
sed 's/正则表达式/替换内容/修饰符' file
⚙️ 二、基本替换命令:s///
功能 | 示例 | 说明 |
---|---|---|
简单替换 | sed 's/apple/banana/' |
每行替换第一个匹配项 |
全局替换 | sed 's/apple/banana/g' |
替换每行所有匹配 |
替换后输出 | sed -n 's/apple/banana/p' |
不显示原文,只显示替换结果 |
就地修改 | sed -i 's/apple/banana/g' file.txt |
直接修改文件内容(危险操作!) |
🧩 三、正则表达式在 sed
中的作用
sed
默认支持 基本正则表达式(BRE) ,
若要使用 扩展正则表达式(ERE) (如 +
, ?
, |
),需加 -E
选项。
bash
sed -E 's/[0-9]+/NUM/g'
🧱 四、分组与反向引用(重点🔥)
在 sed
中,括号需要转义:
\(
和\)
表示"分组"\1
,\2
, ... 表示反向引用(即捕获组)
示例 1:交换字段位置
输入文件:
first last
Tom Smith
Alice Brown
命令:
bash
sed 's/\([A-Za-z]*\) \([A-Za-z]*\)/\2, \1/' names.txt
输出:
Smith, Tom
Brown, Alice
🧩 解释:
\([A-Za-z]*\)
第一个名字\([A-Za-z]*\)
第二个名字\2, \1
表示"第二组,逗号,第一组"
示例 2:提取数字部分
bash
echo "Order1234" | sed 's/[^0-9]*\([0-9]*\).*/\1/'
输出:
1234
解释:
[^0-9]*
→ 非数字开头部分\([0-9]*\)
→ 捕获数字部分.*
→ 后续任意内容\1
→ 返回数字组内容
🧮 五、使用分隔符灵活替换
默认分隔符是 /
,但路径中常含 /
,
可以用其他符号(如 #
、@
)代替:
bash
sed 's#/usr/local/bin#/opt/tools#g'
这样就不必对斜杠转义了。
🔍 六、匹配行:只替换特定模式
bash
sed '/^user/ s/enabled/disabled/' config.txt
含义:
- 只处理以
user
开头的行; - 将
enabled
替换为disabled
。
🧰 七、删除行与插入行
操作 | 示例 | 效果 |
---|---|---|
删除匹配行 | sed '/^#/d' |
删除所有以 # 开头的注释行 |
删除空行 | sed '/^$/d' |
删除空行 |
在匹配后插入行 | sed '/pattern/a\New line here' |
在匹配行后加内容 |
在匹配前插入行 | sed '/pattern/i\Insert before' |
在匹配行前加内容 |
🧠 八、使用"多命令"模式:-e
或 {}
示例:
bash
sed -e 's/foo/bar/' -e 's/hello/hi/'
或:
bash
sed '/^user/ {
s/enabled/disabled/
s/guest/standard/
}'
🧩 九、sed -E
:扩展正则更强大
支持 +
, ?
, |
, ()
(不用加 \
):
bash
sed -E 's/(cat|dog)/animal/g' pets.txt
匹配 cat
或 dog
并替换为 animal
。
🔄 十、使用反向引用进行复杂替换
示例:日期格式转换
输入:
2025-10-10
命令:
bash
sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/'
输出:
10/10/2025
🧩 拆解:
([0-9]{4})
→ 年([0-9]{2})
→ 月([0-9]{2})
→ 日
组合为\3/\2/\1
🧠 十一、匹配多行(进阶)
标准 sed
一次处理一行,但可用 N
命令拼接多行。
例如:合并相邻两行
bash
sed 'N; s/\n/ /'
🧮 十二、配合 Shell 正则使用
bash
for file in *.log; do
sed -i -E 's/error|fail/ERROR/g' "$file"
done
对所有 .log
文件,将 "error" 或 "fail" 替换为大写 "ERROR"。
🧾 十三、实战综合例子
示例:清洗 Nginx 访问日志
目标:提取 IP 和访问路径
日志行:
192.168.1.10 - - [10/Oct/2025:10:20:30 +0800] "GET /index.html HTTP/1.1" 200 512
命令:
bash
sed -E 's/^([0-9.]+).*"GET ([^ ]*).*/\1\t\2/' access.log
输出:
192.168.1.10 /index.html
🧰 十四、修饰符总结
修饰符 | 作用 |
---|---|
g |
全局替换(每行所有匹配) |
p |
打印匹配结果 |
i |
不区分大小写 |
w file |
将匹配结果写入文件 |
I |
大小写不敏感(GNU sed) |
✅ 十五、总结与建议
技能 | 关键知识点 |
---|---|
替换 | s/regex/replace/g |
捕获组 | \( \) 与 \1 |
行过滤 | /pattern/ |
分隔符 | s#old#new# |
扩展正则 | sed -E |
就地修改 | -i |
多命令 | { ... } |
shell编程中基本正则与扩展正则需不需加反斜杠的问题
为什么有的正则要加反斜杠、有的不需要。
🧩 一、Shell 正则的两大"体系"
在 Shell(尤其是 grep
, sed
, awk
等工具)中,
实际上存在 两种正则表达式语法体系:
名称 | 英文全称 | 常见命令参数 | 特点 |
---|---|---|---|
基本正则表达式 | BRE(Basic Regular Expression) | grep , sed 默认使用 |
一些元字符(如 + , ? , {} )需要反斜杠转义 |
扩展正则表达式 | ERE(Extended Regular Expression) | egrep , grep -E , sed -E , awk |
更多元字符直接使用,不需要反斜杠 |
🧠 二、区别核心总结一句话
在 基本正则(BRE) 里,
+
,?
,{}
,|
,()
等特殊符号 默认是普通字符,必须加反斜杠才能生效。在 扩展正则(ERE) 里,这些符号 直接是特殊符号,不需要反斜杠。
📘 三、举例对比:BRE vs ERE
功能 | 基本正则(BRE) | 扩展正则(ERE) | 说明 |
---|---|---|---|
匹配一个或多个 a |
a\+ |
a+ |
+ 在 BRE 需转义 |
匹配零个或一个 a |
a\? |
a? |
? 在 BRE 需转义 |
匹配 2~4 个 a |
a\{2,4\} |
a{2,4} |
{} 在 BRE 需转义 |
匹配 "abc" 或 "xyz" | `abc | xyz` | abc|xyz |
捕获分组 | \(ab\) |
(ab) |
() 在 BRE 需转义 |
🧪 四、实战示例对比
假设有文件 test.txt
内容:
abc
aaa
ab
xyz
① 基本正则(默认 grep)
bash
grep 'a\+' test.txt
✅ 匹配含一个或多个 a 的行。
bash
grep 'a+' test.txt
❌ 错误,因为在 BRE 中 +
被当作普通字符。
② 扩展正则(grep -E 或 egrep)
bash
grep -E 'a+' test.txt
✅ 正确,匹配一个或多个 a。
bash
grep -E 'a\+' test.txt
❌ 错误,因为在 ERE 中 \+
等价于 "匹配字符 +"。
⚙️ 五、与命令工具的关系
命令 | 默认使用 | 启用扩展正则方式 |
---|---|---|
grep |
基本正则(BRE) | grep -E 或 egrep |
sed |
基本正则(BRE) | sed -E |
awk |
扩展正则(ERE) | 默认就是扩展正则,无需 -E |
grep -P |
PCRE(Perl Compatible Regex) | 支持更复杂的语法(如 \d , (?= ) ) |
🧩 六、易混点说明
场景 | 原因 | 解决方案 |
---|---|---|
grep "a{2}" file 报错 |
在 BRE 中 {} 非特殊符号 |
用 grep "a\{2\}" file |
grep -E "a{2}" file 正常 |
扩展正则允许 {} 无需转义 |
✅ |
sed "s/a\+/b/g" 正常? |
取决于 sed 版本 |
GNU sed -E 推荐使用 |
awk '/a+/' file 正常 |
awk 默认使用扩展正则 |
✅ |
🧬 七、思维导图总结
正则表达式体系
├── 基本正则(BRE)
│ ├── 命令:grep, sed
│ ├── 特性:+ ? { } ( ) | 需加 \
│ └── 示例:a\+, a\?, \(ab\)
│
└── 扩展正则(ERE)
├── 命令:grep -E, egrep, sed -E, awk
├── 特性:直接支持 + ? { } ( ) |
└── 示例:a+, a?, (ab), a{2,4}, a|b
✅ 八、结论总结
项目 | 基本正则(BRE) | 扩展正则(ERE) |
---|---|---|
常用命令 | grep , sed |
grep -E , egrep , sed -E , awk |
是否需加反斜杠 | ✅ 是 | ❌ 否 |
适合场景 | 兼容旧系统 | 日常推荐使用 |
推荐写法 | grep -E + 不加反斜杠 |
✅ 推荐现代写法 |
🔎 一句话总结:
在 Shell 正则中,"是否需要加反斜杠"取决于你使用的命令是否启用了扩展正则(ERE)。
grep
、sed
默认使用基本正则(需加\
),而grep -E
、awk
、sed -E
使用扩展正则(不需\
)。