正则表达式与文本三剑客(grep、sed、awk)基础与实践
正则表达式基础与实践
一、正则表达式概述
1. 定义
正则表达式(Regular Expression,简称 RE)是用于描述字符排列和匹配模式的语法规则,核心作用是对字符串进行分割、匹配、查找、替换操作。它本质是 “模式模板”,Linux 工具(如 grep、egrep、awk)可通过该模板过滤文本数据流,最终输出 “匹配的数据” 或 “滤掉的数据”。
2. 应用场景
- 判断字符串是否符合特定格式(如手机号、邮箱、日期);
- 从大量文本中提取目标内容(如日志中的 IP 地址、错误信息);
- 批量替换文本中的指定字符或格式。
3. 组成结构
正则表达式由 普通字符 和 元字符 组成:
- 普通字符:大小写字母(A-Z、a-z)、数字(0-9)、标点符号(!、@、# 等)及其他常规符号,仅匹配自身;
- 元字符:具有特殊意义的专用字符,用于规定前导字符的出现模式(如匹配开头、结尾、重复次数等)。
二、基础正则表达式(BRE)
基础正则表达式是正则的核心语法,适用于 grep、sed 等工具,以下是常用元字符及示例:
元字符 | 功能说明 | 示例 | 示例说明 |
\ | 转义字符,取消后续特殊符号的含义 | \!、\$、\n | 匹配 !、$、换行符 \n |
^ | 匹配字符串的开始位置 | ^a、^the、^# | 匹配以 a、the、# 开头的行 |
$ | 匹配字符串的结束位置 | word$、^$ | 匹配以 word 结尾的行;^$ 匹配空行 |
. | 匹配除换行符 \n 以外的任意一个字符 | lo.k、l..k | 匹配 lok(o 和 k 间 1 个任意字符)、lbck(l 和 k 间 2 个任意字符) |
* | 匹配前面一个字符出现0 次或多次(可重复任意次,包括不出现) | loo*k、lo*k | loo*k 匹配 lk(0 个 o)、lok(1 个 o)、look(2 个 o)等;lo*k 同理 |
[list] | 匹配 list 列表中的任意一个字符(列表可是字符集、范围) | go[ola]d、[a-z]、[0-9] | go[ola]d 匹配 gold、goal、god;[0-9] 匹配任意 1 位数字 |
[^list] | 匹配非 list 列表中的任意一个字符(取反) | [^0-9]、[^a-z] | [^0-9] 匹配非数字字符;[^a-z] 匹配非小写字母 |
\{n\} | 匹配前面一个字符恰好 n 次(grep 需加 \,egrep/awk 无需加) | lo\{2\}k、[0-9]\{2\} | lo\{2\}k 匹配 look(o 出现 2 次);[0-9]\{2\} 匹配任意 2 位数字 |
\{n,\} | 匹配前面一个字符不少于 n 次(≥n 次) | lo\{2,\}k、[0-9]\{2,\} | lo\{2,\}k 匹配 look(2 次 o)、loook(3 次 o)等;[0-9]\{2,\} 匹配 2 位及以上数字 |
\{n,m\} | 匹配前面一个字符n 到 m 次(≥n 且 ≤m 次) | lo\{2,3\}k、[0-9]\{2,3\} | lo\{2,3\}k 匹配 look(2 次 o)、loook(3 次 o);[0-9]\{2,3\} 匹配 2-3 位数字 |
基础正则实践(基于 a.txt 文本)
1. 准备测试文本 a.txt
先创建文本文件,内容如下:
lkloklookloooklooooookloooooaaaklooooooookabbbbcdabbbbcd666ooooloooookoooooolkaoblck
2. 实践案例(grep 命令)
命令 | 匹配逻辑 | 输出结果(a.txt 中匹配的行) |
grep "loo*k" a.txt | 匹配 l 后接任意次 o(0 次及以上),再接 k | lok、look、loook、looooook、looooooook、ooooloooook |
grep "lo.k" a.txt | 匹配 l 后接 o、1 个任意字符,再接 k | look(o 和 k 间是 o,符合 “1 个任意字符”) |
grep "lo.*k" a.txt | 匹配 l 后接 o、任意个任意字符(0 次及以上),再接 k | lok、look、loook、looooook、loooooaaak、looooooook、ooooloooook(o 和 k 间可含字母如 aaa) |
grep "lo\{2\}k" a.txt | 匹配 l 后接 2 次 o,再接 k | look |
grep "lo\{2,\}k" a.txt | 匹配 l 后接≥2 次 o,再接 k | look、loook、looooook、looooooook、ooooloooook |
grep "lo\{2,3\}k" a.txt | 匹配 l 后接 2-3 次 o,再接 k | look(2 次 o)、loook(3 次 o) |
grep "^a" a.txt | 匹配以 a 开头的行 | abbbbcd、abbbbcd666、aoblck |
grep "6$" a.txt | 匹配以 6 结尾的行 | abbbbcd666 |
三、扩展正则表达式(ERE)
扩展正则表达式在基础正则上新增了元字符,功能更灵活,适用于 egrep(grep -E)、awk 等工具(无需对 {}、+ 等元字符转义,grep 需加 \ 转义)。
元字符 | 功能说明 | 示例 | 示例说明 |
+ | 匹配前面一个字符出现1 次及以上(至少出现 1 次,区别于 * 的 “0 次及以上”) | lo+k | 匹配 lok(1 次 o)、look(2 次 o)、loook(3 次 o)等,不匹配 lk(0 次 o) |
? | 匹配前面一个字符出现0 次或 1 次(最多出现 1 次) | lo?k | 匹配 lk(0 次 o)、lok(1 次 o),不匹配 look(2 次 o) |
() | 将括号中的字符串作为一个整体(分组),用于批量匹配或重复 | l(oo)+k | 把 oo 视为整体,匹配 look(1 次 oo)、looook(2 次 oo)等 |
` | ` | 逻辑 “或”,匹配 ` | ` 两侧任意一个表达式 |
{n} | 同基础正则 \{n\},匹配前面字符恰好 n 次(无需转义) | lo{3}k | 匹配 loook(o 出现 3 次) |
{n,} | 同基础正则 \{n,\},匹配前面字符≥n 次(无需转义) | lo{3,}k | 匹配 loook(3 次 o)、looooook(5 次 o)等 |
{n,m} | 同基础正则 \{n,m\},匹配前面字符 n-m 次(无需转义) | lo{3,5}k | 匹配 loook(3 次 o)、looooook(5 次 o),不匹配 looooooook(6 次 o) |
扩展正则实践(基于 a.txt 文本)
使用 egrep 命令(支持扩展正则,无需转义),实践案例如下:
命令 | 匹配逻辑 | 输出结果(a.txt 中匹配的行) |
egrep "lo+k" a.txt | 匹配 l 后接≥1 次 o,再接 k | lok、look、loook、looooook、looooooook、ooooloooook |
egrep "lo?k" a.txt | 匹配 l 后接 0-1 次 o,再接 k | lk(0 次 o)、lok(1 次 o)、oooooolk(ooooo 后接 o?,即 0-1 次 o,最终匹配 oooooolk) |
egrep "l(oo)+k" a.txt | 匹配 l 后接≥1 次 oo 组,再接 k | look(1 次 oo)、looooook(2 次 oo)、looooooook(3 次 oo)、ooooloooook(含 oo 组) |
`egrep "(oo | ab)" a.txt` | 匹配含 oo 或 ab 的行 |
egrep "lo{3}k" a.txt | 匹配 l 后接 3 次 o,再接 k | loook |
egrep "lo{3,}k" a.txt | 匹配 l 后接≥3 次 o,再接 k | loook、looooook、looooooook、ooooloooook |
egrep "lo{3,5}k" a.txt | 匹配 l 后接 3-5 次 o,再接 k | loook(3 次 o)、ooooloooook(loooo 即 4 次 o,符合 3-5 次范围) |
四、补充实践(基于 b.txt 文本)
1. 准备测试文本 b.txt
创建文本文件,内容如下:
loklo12klo1kloAkloBklookloaklodkabcd1234
2. 案例(基础 / 扩展正则结合)
命令 | 正则类型 | 匹配逻辑 | 输出结果(b.txt 中匹配的行) |
grep "[a-zA-Z0-9]k" b.txt | 基础 | 匹配 “字母 / 数字 + k” 的组合 | lo1k、loAk、loBk、look、loak、lodk |
grep "lo[ABo]k" b.txt | 基础 | 匹配 lo 后接 A/B/o,再接 k | loAk、loBk、look |
grep "lo[^a-zA-Z]k" b.txt | 基础 | 匹配 lo 后接 “非字母”,再接 k | lo1k(lo 后是数字 1,非字母) |
grep "[^a-zA-Z]" b.txt | 基础 | 匹配含 “非字母” 的行(即含数字、符号等) | lo12k、lo1k、1234 |
五、工具使用说明
工具 | 支持的正则类型 | 特殊说明(元字符转义) |
grep | 基础正则(BRE) | 使用 {n}、{n,}、{n,m} 需加转义 \(如 \{2\});扩展正则需加 -E 参数(grep -E 等价于 egrep) |
egrep | 扩展正则(ERE) | 无需转义 {}、+、?、()、` |
awk | 扩展正则(ERE) | 无需转义,支持所有扩展正则元字符 |
sed | 基础正则(BRE) | 使用扩展正则需加 -r 参数 |
Linux 文本处理三剑客(grep、sed、awk)
一、grep:文本过滤工具
grep 是 Linux 中最基础的文本过滤工具,核心功能是按 “字符串” 或 “正则表达式” 筛选文本行,仅输出匹配的内容,常用于日志分析、配置文件检索等场景。
1. 核心语法
grep [选项] "匹配规则" 目标文件
2. 常用选项与功能
选项 | 功能说明 | 示例 |
无选项 | 直接匹配包含 “字符串” 的行,输出整行内容 | grep "blp5" /tmp/services(输出含 “blp5” 的行) |
-v | 反向匹配:输出不包含“字符串” 的行 | grep -v "udp" /tmp/services(输出不含 “udp” 的行) |
-i | 忽略大小写:匹配时不区分字母大小写 | grep -i "BLP5" /tmp/services(同时匹配 “blp5”“BLP5” 等) |
-o | 仅显示匹配部分:不输出整行,只打印匹配的字符串 | grep -o "48[0-9]\+" /tmp/services(仅输出以 “48” 开头的数字) |
-n | 显示行号:输出匹配行的行号及内容 | grep -n "tcp" /tmp/services(输出含 “tcp” 的行号 + 内容) |
-E | 支持扩展正则表达式(等价于 egrep) | grep -E "lo{2,3}k" a.txt(匹配 “look”“loook”) |
3. 常用匹配规则(结合正则)
匹配规则 | 功能 | 示例 |
^字符串 | 匹配以 “字符串” 开头的行 | grep "^blp5" /tmp/services(输出以 “blp5” 开头的行) |
字符串$ | 匹配以 “字符串” 结尾的行 | grep "6$" a.txt(输出以 “6” 结尾的行) |
^$ | 匹配空行 | grep -v "^$" /etc/httpd/conf/httpd.conf(过滤配置文件空行) |
[字符集] | 匹配字符集中的任意一个字符 | grep "lo[ABo]k" b.txt(匹配 “loAk”“loBk”“look”) |
[^字符集] | 匹配非字符集中的任意一个字符 | grep "lo[^a-z]k" b.txt(匹配 “lo1k”,不匹配 “loak”) |
4. 典型场景示例
- 过滤 Apache 配置文件注释行和空行:
grep -v "^#" /etc/httpd/conf/httpd.conf | grep -v "^$"
- 从日志中提取 IP 地址(假设日志含 “192.168.x.x” 格式 IP):
grep -o "192\.168\.[0-9]\+\.[0-9]\+" /var/log/access.log
二、sed:流编辑器(文本修改工具)
sed(Stream Editor)是 “流编辑器”,核心功能是按规则逐行处理文本(修改、删除、添加、替换),处理时不直接修改原文件(需 -i 选项),常用于批量修改配置、文本格式化等场景。
sed 的核心逻辑:将文本行逐行读入 “模式空间”(临时缓冲区),按命令处理后输出,再处理下一行,直到文件结束。
1. 核心语法
sed [选项] "地址范围 命令" 目标文件
创建文本文件,内容如下:
cat /tmp/services
nimgtw 48003/udp # Nimbus Gateway
3gpp-cbsp 48049/t//cp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
blp5 48129/tcp # Bloomberg locator
blp5 48129/udp # Bloomberg locator
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject
2. 常用选项
选项 | 功能说明 | 示例 |
-n | 不打印模式空间:仅输出被命令处理后的行(需配合 p 命令) | sed -n "1,3p" /tmp/services(仅输出 1-3 行) |
-i | 直接修改原文件:无需重定向,直接覆盖原文件内容(慎用!) | sed -i "s/blp5/test/g" /tmp/services(将原文件中 “blp5” 替换为 “test”) |
-e | 执行多个命令:一次处理中执行多个 sed 命令 | sed -e "1,4d" -e "s/blp5/test/" /tmp/services(先删 1-4 行,再替换字符串) |
-f | 从文件读取命令:将 sed 命令写入文件,通过 -f 调用 | sed -f sed_commands.txt /tmp/services(sed_commands.txt 中存 sed 命令) |
-r | 支持扩展正则表达式:无需对 {} + 等元字符转义 | sed -r "s/(lo{2})k/\1_test/" a.txt(匹配 “look” 并替换为 “loo_test”) |
3. 关键概念:地址范围(指定处理哪些行)
sed 通过 “地址范围” 限定命令作用的行,常见格式如下:
地址范围格式 | 功能 | 示例 |
数字 | 仅处理第 N 行 | sed "5d" /tmp/services(删除第 5 行) |
数字1,数字2 | 处理第 N1 行到第 N2 行 | sed "1,3p" /tmp/services(打印 1-3 行) |
数字,+$N | 从第 N 行开始,向后 N 行 | sed "/blp5/,+1p" /tmp/services(打印 “blp5” 行及后 1 行) |
数字~步长 | 从第 N 行开始,每 “步长” 行处理一次 | sed "1~2d" /tmp/services(删除奇数行,步长 = 2) |
/正则/ | 处理匹配 “正则” 的行 | sed "/^blp5/d" /tmp/services(删除以 “blp5” 开头的行) |
/正则1/,/正则2/ | 处理 “正则 1” 匹配行到 “正则 2” 匹配行 | sed "/^blp5/,/^com/p" /tmp/services(打印 “blp5” 到 “com” 开头的行) |
$ | 处理最后一行 | sed "$d" /tmp/services(删除最后一行) |
4. 常用 sed 命令(处理文本的核心操作)
命令 | 功能 | 示例 |
p | 打印:输出模式空间中的行(需配合 -n) | sed -n "1~2p" /tmp/services(打印奇数行) |
d | 删除:删除模式空间中的行,不输出 | sed "/udp/d" /tmp/services(删除含 “udp” 的行) |
s/旧值/新值/[选项] | 替换:将 “旧值” 替换为 “新值”,默认仅替换每行第一个匹配 | - s/blp5/test/:替换每行第一个 “blp5” 为 “test” - s/blp5/test/g:g(全局替换),替换每行所有 “blp5” - s/48049/&.123/:& 引用匹配内容,将 “48049” 改为 “48049.123” |
a \文本 | 追加:在匹配行下方添加 “文本” | sed "/blp5/a \new_line" /tmp/services(在 “blp5” 行下加 “new_line”) |
i \文本 | 插入:在匹配行上方添加 “文本” | sed "/blp5/i \new_line" /tmp/services(在 “blp5” 行上加 “new_line”) |
c \文本 | 替换行:将匹配行整体替换为 “文本” | sed "/blp5/c \replace_line" /tmp/services(将 “blp5” 行替换为 “replace_line”) |
r 文件名 | 读取文件:将 “文件名” 的内容追加到匹配行下方 | sed "/blp5/r a.txt" /tmp/services(在 “blp5” 行下追加 a.txt 内容) |
w 文件名 | 写入文件:将匹配行写入 “文件名”(不影响屏幕输出) | sed "/blp5/w b.txt" /tmp/services(将 “blp5” 行保存到 b.txt) |
5. 典型场景示例
- 批量替换配置文件中的字符串(修改原文件):
sed -i "s/Listen 80/Listen 8080/g" /etc/httpd/conf/httpd.conf
- 删除日志文件中第 10-20 行的注释(假设注释以 “#” 开头):
sed -i "10,20s/^#//" /var/log/app.log
- 将文本中 “端口 / 协议” 格式(如 48003/udp)改为 “协议 / 端口”(如 udp/48003):
sed -r "s/(.*)([0-9]+)\/(tcp|udp)(.*)/\1\3\/\2\4/" /tmp/services
三、awk:文本处理编程语言
awk 是功能最强大的文本处理工具,兼具 “编程语言” 特性,支持字段分割、变量运算、条件判断、循环等,核心优势是 “按字段处理文本”(如表格数据、日志的字段提取),常用于数据统计、报表生成等场景。
awk 的核心逻辑:将文本逐行视为 “记录”,每行按 “分隔符” 拆分为多个 “字段”(默认分隔符为空格 / 制表符),通过 $1 $2... 引用字段($0 表示整行)。
1. 核心语法
awk [选项] '模式 { 动作 }' 目标文件
- 模式:指定处理哪些行(如 BEGIN END、正则、行号范围);
- 动作:对匹配的行执行的操作(如打印字段、运算、判断)。
2. 常用选项
选项 | 功能说明 | 示例 |
-F 分隔符 | 指定字段分隔符(默认是空格 / 制表符) | awk -F ":" '{print $1,$3}' /etc/passwd(以 “:” 为分隔符,打印第 1、3 字段) |
-v 变量=值 | 定义 awk 变量(在处理文本前赋值) | awk -v num=5 '$3 > num {print $0}' /etc/passwd(打印第 3 字段大于 5 的行) |
-f 文件名 | 从文件读取 awk 脚本(将 “模式 + 动作” 写入文件) | awk -f script.awk /tmp/services(script.awk 中存 awk 逻辑) |
--profile=[文件] | 将 awk 命令格式化输出到文件(便于调试) | awk --profile=prof.txt 'BEGIN{print "test"}'(将脚本写入 prof.txt) |
3. 核心模式(控制处理范围)
模式 | 功能 | 示例 |
BEGIN { 动作 } | 处理文本前执行(仅执行一次),常用于初始化变量、打印表头 | awk 'BEGIN{print "Service\tPort"; print "==="}' /tmp/services(先打印表头) |
END { 动作 } | 处理文本后执行(仅执行一次),常用于统计结果、打印表尾 | awk '{count++} END{print "总行数:"count}' /tmp/services(统计文件总行数) |
/正则/ | 处理匹配 “正则” 的行 | awk '/tcp/{print $1,$2}' /tmp/services(打印含 “tcp” 的行的第 1、2 字段) |
行号范围 | 处理指定行号的行 | awk '1,3{print $0}' /tmp/services(打印 1-3 行) |
条件判断 | 处理满足条件的行 | awk '$2 ~ /48129/{print $0}' /tmp/services(第 2 字段含 “48129” 的行) |
4. 常用内置变量(无需定义直接使用)
内置变量 | 功能 | 示例 |
$0 | 表示当前行的完整内容 | awk '{print $0}' /tmp/services(打印整行) |
$n | 表示当前行的第 n 个字段(n 为数字) | awk -F ":" '{print $1}' /etc/passwd(打印用户名,第 1 字段) |
NF | 表示当前行的 “字段总数” | awk '{print "当前行字段数:"NF}' /tmp/services(输出每行的字段数) |
NR | 表示当前行的 “行号”(整个文件的行号) | awk 'NR>5{print NR,$0}' /tmp/services(打印行号大于 5 的行) |
FS | 字段分隔符(等价于 -F 选项,默认是空格) | awk 'BEGIN{FS=":"} {print $1,$3}' /etc/passwd(以 “:” 为分隔符) |
OFS | 输出字段分隔符(默认是空格) | `awk 'BEGIN{FS=":";OFS=" |
5. 典型场景示例
- 统计 /etc/passwd 中 UID 大于 1000 的用户数:
awk -F ":" '$3 > 1000 {count++} END{print "普通用户数:"count}' /etc/passwd
- 从 Apache 访问日志(假设格式为 “IP 时间 请求路径”)中提取访问最多的前 5 个 IP:
awk '{print $1}' /var/log/access.log | sort | uniq -c | sort -nr | head -5
- 格式化输出 /tmp/services 内容(添加表头、对齐字段):
awk 'BEGIN{print "Service\t\tPort/Protocol\tDescription";print "----------------------------------------"} {printf "%-15s %-15s %s\n", $1, $2, $3}' /tmp/services
结果
Service Port Description
===
nimgtw 48003/udp # Nimbus Gateway
3gpp-cbsp 48049/tcp # 3GPP Cell Broadcast Service Protocol
isnetserv 48128/tcp # Image Systems Network Services
isnetserv 48128/udp # Image Systems Network Services
blp5 48129/tcp # Bloomberg locator
blp5 48129/udp # Bloomberg locator
com-bardac-dw 48556/tcp # com-bardac-dw
com-bardac-dw 48556/udp # com-bardac-dw
iqobject 48619/tcp # iqobject
iqobject 48619/udp # iqobject
===
END......
- 计算文本中第 2 字段(假设是数字)的总和:
awk '{sum += $2} END{print "总和:"sum}' /tmp/services
四、三剑客功能对比与适用场景
工具 | 核心优势 | 适用场景 | 特点 |
grep | 快速过滤文本行 | 日志检索、关键词匹配、筛选行 | 功能单一,仅 “过滤”,不修改内容 |
sed | 批量修改文本 | 字符串替换、行删除 / 添加、格式调整 | 按 “行” 处理,适合简单修改,不擅长字段运算 |
awk | 字段处理与数据统计 | 字段提取、数据计算、报表生成、条件判断 | 按 “字段” 处理,支持编程逻辑,功能最全面 |
总结:简单筛选用 grep,批量修改用 sed,复杂字段处理或统计用 awk,实际场景中三者常结合使用(如 grep 筛选后用 sed 修改,再用 awk 统计)。