Shell编程之正则表达式与文本处理工具
一、正则表达式基础
1. 正则表达式概述
定义:正则表达式(Regular Expression,简称Regex)是由普通字符(如字母、数字、标点符号)与元字符(具有特殊含义的专用字符)组成的字符串匹配规则,用于判断某字符串是否符合特定格式要求。
核心作用:在Shell脚本中,通过文本处理工具(如grep
、sed
、awk
)结合正则表达式,可以快速筛选、提取或修改目标文本内容,是自动化运维、日志分析、数据清洗等场景的基础技能。
类比理解:正则表达式类似于“文本搜索的精准模板”——普通字符按字面意义匹配,元字符则定义了匹配的“规则模式”(如“任意字符”“重复次数”“开头/结尾位置”等)。
2. 正则表达式分类
根据POSIX(可移植操作系统接口)标准,正则表达式分为两大类,主要区别在于元字符的语法格式(是否需要转义)及支持的工具:
类型 | 支持工具 | 核心区别 | 典型使用场景 |
---|---|---|---|
基本正则表达式(BRE,Basic Regular Expression) |
| 元字符(如 | 简单文本过滤(如查找固定模式的行)。 |
扩展正则表达式(ERE,Extended Regular Expression) |
| 元字符(如 | 复杂模式匹配(如分组、逻辑“或”操作)。 |
关键区别示例:
匹配连续2个
o
:
BRE(需转义):
go\{2\}d
(如grep "go\{2\}d" file
)ERE(无需转义):
go{2}d
(如egrep "go{2}d" file
或grep -E "go{2}d" file
)
工具默认行为:
grep
:默认使用BRE,若需使用ERE需加-E
参数(或直接使用egrep
)。sed
:默认使用BRE,若需使用ERE需加-r
参数(部分新版本支持-E
)。awk
:默认支持大部分ERE语法(如+
、?
、|
无需转义)。
二、正则表达式核心元字符详解
1. 通用元字符(BRE与ERE均适用)
这些元字符在基本正则和扩展正则中功能一致,但部分在BRE中需要转义。
元字符 | 含义 | 用法示例 | 匹配示例 |
---|---|---|---|
| 匹配字符串的开头位置 |
|
|
| 匹配字符串的结尾位置 |
|
|
| 匹配除换行符 | ||
之外的任意单个字符 |
|
| |
| 匹配前一个字符的0次或多次重复 |
|
|
`` | 转义字符,用于取消特殊字符的元字符含义(如让 |
|
|
注意:在BRE(如默认
grep
/sed
)中,元字符如{
、}
、+
、?
、|
需通过\`转义才具有特殊含义;而在ERE(如
egrep/
grep -E/
awk`)中,这些元字符通常无需转义。
2. 字符集合匹配(指定字符范围内的选择)
用于匹配一组字符中的任意一个,或排除某些字符。
元字符 | 含义 | 用法示例 | 匹配示例 |
---|---|---|---|
| 匹配方括号内列出的任意一个字符(字符列表)。 |
|
|
| 匹配不在方括号内的任意一个字符(取反集合)。 |
|
|
示例:
[0-9]{3}
:匹配连续3个数字(如123
)。
[^ ]
:匹配非空格的任意字符(如字母、符号)。
3. 次数匹配(控制前一个字符的重复次数)
用于精确控制某个字符或字符集合的重复次数范围。
元字符 | 含义 | 用法示例 | 匹配示例 |
---|---|---|---|
| 匹配前一个字符恰好n次(BRE需转义,ERE可直接用 |
|
|
| 匹配前一个字符至少n次(BRE需转义,ERE可直接用 |
|
|
| 匹配前一个字符n到m次(BRE需转义,ERE可直接用 |
|
|
关键区别:
在BRE(如默认
grep
/sed
)中,{
和}
是普通字符,需转义为\{
和\}
才能表示次数匹配。在ERE(如
egrep
/grep -E
/awk
)中,直接使用{n}
、{n,}
等无需转义。
示例对比:
BRE命令:
grep "go\{2\}d" file
(匹配gooood
)。ERE命令:
egrep "go{2}d" file
或grep -E "go{2}d" file
(效果相同,无需转义)。
4. 扩展正则表达式特有元字符(仅ERE支持)
扩展正则表达式在BRE基础上增加了更灵活的操作符,简化了复杂模式的编写。
元字符 | 含义 | 用法示例 | 匹配示例 |
---|---|---|---|
| 匹配前一个字符1次或多次(至少1次,相当于 |
|
|
| 匹配前一个字符0次或1次(最多1次,相当于 |
|
|
| 将括号内的内容视为一个整体(用于分组操作,如重复、逻辑或)。 |
|
|
` | ` | 逻辑“或”匹配(匹配多个模式中的任意一个,类似“或”关系)。 |
|
注意:
()
和|
在BRE中无效(除非使用\(\)
和\|
转义),仅在ERE中直接支持。
三、正则表达式核心元字符速查表
类别 | 元字符 | 功能 | 是否需要转义(BRE) | ERE中是否转义 |
---|---|---|---|---|
位置匹配 |
| 行首 | 否 | 否 |
| 行尾 | 否 | 否 | |
单字符匹配 |
| 任意单个字符(除` | ||
`) | 否 | 否 | ||
`` | 转义特殊字符 | 是(需转义其他元字符) | 是(按需转义) | |
字符集合 |
| 匹配列表中的任意一个字符 | 否 | 否 |
| 匹配不在列表中的任意一个字符 | 否 | 否 | |
次数匹配 |
| 前一个字符0次或多次 | 否 | 否 |
| 前一个字符恰好n次(BRE) | 是(需加``) | 否(直接 | |
| 前一个字符至少n次(BRE) | 是(需加``) | 否(直接 | |
| 前一个字符n到m次(BRE) | 是(需加``) | 否(直接 | |
| 前一个字符恰好n次(ERE) | 否(ERE无需转义) | 否 | |
扩展功能 |
| 前一个字符1次或多次 | 否(仅ERE支持) | 否 |
| 前一个字符0次或1次 | 否(仅ERE支持) | 否 | |
| 分组(整体操作) | 否(仅ERE支持) | 否 | |
` | `(ERE) | 逻辑“或”匹配 | 否(仅ERE支持) |
四、正则表达式实战案例
案例1:电话号码匹配(BRE)
需求:匹配区号025
开头的电话号码,要求:
号码与区号间分隔符为空格、
-
或无分隔;号码本身必须是5或8开头,且为8位数字。
正则表达式:^(025)[ -]?[58][0-9]{7}$
分解说明:
^
:匹配行首;(025)
:匹配固定区号025
(分组非必须,但提高可读性);[ -]?
:匹配分隔符(空格或-
),?
表示该分隔符出现0次或1次(即可以没有分隔符);[58]
:匹配号码首数字为5
或8
;[0-9]{7}
:匹配后续7位数字(共8位);$
:匹配行尾。
测试数据(phone.txt):
02588888888
025-5555555555
025 12345678
025 54321678
025ABC88888
025-85432109
028-85643210
0251-52765421
匹配命令:
egrep "^(025)[ -]?[58][0-9]{7}$" phone.txt
预期输出:
02588888888
025 54321678
025-85432109
案例2:电子邮箱匹配(ERE)
需求:匹配符合标准格式的邮箱(用户名@子域名.二级域名.顶级域
),要求:
用户名:6-18位,以字母或下划线开头,允许字母、数字、部分符号(不含
@
和空格);子域名与二级域名:允许字母、数字、
-
、_
、.
;顶级域:2-5位字母。
正则表达式:^([a-zA-Z_][^@ ]{5,17})@[a-zA-Z0-9_\-.]+(\.[a-zA-Z0-9_\-.]+)?\.([a-zA-Z]{2,5})$
分解说明:
^
:行首;([a-zA-Z_][^@ ]{5,17})
:用户名部分:[a-zA-Z_]
:首字符为字母或下划线;[^@ ]{5,17}
:后续5-17位为非@
、非空格的任意字符(总长度6-18位);
@
:固定符号;[a-zA-Z0-9_\-.]+
:子域名/二级域名(允许字母、数字、_
、-
、.
,至少1个字符);(\.[a-zA-Z0-9_\-.]+)?
:可选的二级域名(以.
开头,后接合法字符,?
表示0或1次);\.([a-zA-Z]{2,5})
:顶级域(以.
开头,后接2-5位字母);$
:行尾。
测试数据(email.txt):
zhangsan123@qq.com
li si@163.com
wang@wu@sina.com
zhao liu@126.com
qianqi@sina.com.cn
匹配命令:
egrep "^([a-zA-Z_][^@ ]{5,17})@[a-zA-Z0-9_\-.]+(\.[a-zA-Z0-9_\-.]+)?\.([a-zA-Z]{2,5})$" email.txt
预期输出:
zhangsan123@qq.com
qianqi@sina.com.cn
五、文本处理工具详解
1. sed流编辑器(Stream Editor)
(1)核心概念
sed是一种非交互式流编辑器,它逐行读取输入文本(文件或管道),根据预定义的规则(命令)对每行内容进行编辑(如删除、替换、插入),并将结果输出到屏幕或文件。默认情况下,sed不会修改原文件(除非使用-i
选项)。
工作流程:
读取:从输入源(文件/标准输入)读取一行内容,存入模式空间(临时缓冲区);
执行:在模式空间中按顺序执行所有sed命令(若指定行地址,则只对该行或行范围执行);
显示:将处理后的模式空间内容输出到结果流(默认输出到屏幕),然后清空模式空间;
循环:重复上述步骤,直到处理完所有输入行。
特点:适合批量处理文本(如日志清洗、配置文件修改),尤其擅长基于行或模式的简单编辑操作。
(2)常用命令格式
sed [选项] '操作命令' 文件1 文件2...
# 常用选项:
# -n:禁止默认输出(仅显示显式指定的内容,需配合p命令使用)
# -i:直接修改原文件(谨慎使用!建议先备份)
# -r 或 -E:支持扩展正则表达式(-r为旧版,-E为新版推荐)
# -e:指定多个操作命令(单命令时可省略)
(3)常用操作与示例
操作 | 功能 | 示例命令 | 详细说明 |
---|---|---|---|
| 打印当前模式空间的内容 |
| 需配合 |
| 删除当前行 |
| 直接移除匹配的行,不进入输出流。 |
| 替换字符 |
|
|
| 在当前行后追加内容 |
| 追加的文本会作为新行出现在指定行之后。 |
| 在当前行前插入内容 |
| 插入的文本会作为新行出现在指定行之前。 |
| 打印当前行号 |
| 仅输出行号,不输出行内容。 |
| 读取下一行 |
|
|
(4)寻址方式(指定操作的目标行)
sed支持通过行号或正则表达式指定需要操作的行范围。
寻址方式 | 示例命令 | 说明 |
---|---|---|
单行行号 |
| 精确操作某一行。 |
最后一行 |
|
|
行范围 |
| 操作从起始行到结束行的所有行。 |
相对行范围 |
| 从指定行开始,连续操作后续N行。 |
正则匹配行 |
| 操作所有匹配正则表达式的行。 |
组合条件 |
| 从指定行开始,到匹配某个模式的行结束。 |
(5)高级操作示例
删除空行:
sed '/^$/d' file.txt # 删除所有空白行(行内容为空)
注释特定行(如给以
root
开头的行添加#
注释):sed '/^root/ s/^/#/' /etc/passwd # 将/etc/passwd中以root开头的行首添加#
直接修改文件(谨慎使用!):
sed -i 's/old/new/g' file.txt # 将文件中所有old替换为new,并直接保存到原文件
复杂替换(保留匹配内容):
sed -n 's/\/bin\/bash/\/bin\/csh/p' /etc/passwd # 将/bin/bash替换为/bin/csh,并打印修改的行 # 使用\转义/,或改用其他分隔符(如|)避免冲突: sed -n 's|/bin/bash|/bin/csh|p' /etc/passwd
2. awk文本分析工具(Pattern-Scan Processing Language)
(1)核心概念
awk是一种面向字段的文本处理工具,它逐行读取输入文本,默认以空格或Tab键为分隔符将每行拆分为多个字段(1,2, ..., $NF),并通过模式(条件)+ 操作(命令)的方式对符合条件的行进行灵活处理。awk更适合处理结构化数据(如日志、CSV文件),支持复杂的逻辑判断、数学运算和循环控制。
特点:擅长按字段提取、统计和分析文本(如计算平均值、筛选特定条件的行、格式化输出)。
(2)基本语法
awk [选项] '模式或条件 {操作语句}' 文件1 文件2...
# 常用选项:
# -F:指定字段分隔符(如-F: 表示以冒号为分隔符)
执行流程:
逐行读取输入文本;
根据指定的分隔符(默认空格/TAB)将当前行拆分为多个字段(1,2,...NF,0表示整行);
若未指定模式,则对所有行执行操作;若指定了模式(如
/关键词/
、NR>10
),则仅对匹配模式的行执行操作;在操作语句中,可通过内置变量(如$1、NF、NR)访问字段和行信息,并执行打印、计算等操作。
(3)常用内置变量
变量 | 含义 | 示例 |
---|---|---|
| 当前行的完整内容 |
|
| 当前行的第1~n个字段 |
|
| 当前行的字段总数 |
|
| 当前处理的行号(从1开始) |
|
| 输入字段分隔符(同-F选项) |
|
| 输出字段分隔符(默认空格) | `awk '{OFS=" |
(4)常用操作与示例
场景 | 命令示例 | 详细说明 |
---|---|---|
打印所有内容 |
| 打印文件的每一行($0表示整行内容)。 |
打印指定行 |
| 通过行号(NR)精确控制要输出的行。 |
打印行范围 |
| 操作从第1行到第3行的所有内容。 |
打印奇数行/偶数行 | 奇数行: | 通过取模运算(NR%2)判断行号的奇偶性。 |
按字段筛选 |
| 条件判断字段值(如第3字段UID),并输出指定字段。 |
统计匹配行数 |
|
|
格式化输出 |
| 自定义输出格式(结合字符串拼接)。 |
多分隔符支持 |
| 方括号内指定多个分隔符(逻辑“或”关系)。 |
(5)高级功能
调用Shell命令:通过管道将字段传递给外部命令(如统计在线用户数):
awk 'BEGIN{n=0; while("w" | getline) n++; print n-2}' # 统计当前登录用户数(扣除标题和空行)
数学运算:计算某列的平均值(如第一列数字的平均值):
awk '{sum+=$1; count++} END {print sum/count}' data.txt # sum累加第一列,count统计行数
数组统计:统计每行首字段的出现次数并排序:
awk '{a[$1]++} END {for(i in a) print a[i],i}' test.txt # 统计每个首字段的出现次数 awk '{a[$1]++} END {for(i in a) print a[i],i}' test.txt | sort -r # 按次数降序排序
条件判断与三元运算符:
awk -F: '{max=($3>$4)?$3:$4; print max}' /etc/passwd # 取第3字段和第4字段的最大值
内置函数:如
length($0)
(计算行长度)、tolower($1)
(转小写)、substr($1,2,3)
(截取子串)。
六、总结对比
工具 | 核心定位 | 主要功能 | 正则表达式支持 | 典型使用场景 |
---|---|---|---|---|
| 快速文本搜索与过滤 | 根据模式匹配查找包含特定内容的行(支持过滤日志、查找关键字) | BRE(默认)、ERE( | 日志分析、关键词查找、空行/注释过滤 |
| 流式文本编辑 | 对文本行进行删除、替换、插入等操作(适合批量修改配置文件、清理日志) | BRE(默认)、ERE( | 配置文件修改、行内容替换、注释处理 |
| 结构化文本分析与处理 | 按字段拆分文本,支持条件判断、数学运算、复杂逻辑(适合数据统计、报表生成) | ERE(默认支持高级正则) | 日志统计、字段提取、数据格式化 |