CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Shell 与 Bash 入门/课时/正则表达式
RawBlame
课时目录12 节

Shell 与 Bash 入门

  1. 01Shell 是什么
  2. 02目录与文件导航
  3. 03文件操作与权限
  4. 04管道与文本处理
  5. 05变量与字符串
  6. 06条件与循环
  7. 07函数与脚本结构
  8. 08正则表达式
  9. 09进程与定时任务
  10. 10网络命令
  11. 11系统管理与编辑器
  12. 12综合练习

正则表达式

基本/扩展正则、grep 实战。

30 分钟

正则表达式

这一课你将能做到

  1. 看懂并手写 BRE 与 ERE 两种风格的正则,说出 grep 默认用哪种、-E 切换到哪种
  2. 用正则从日志中提取 IP、时间戳、状态码,校验手机号、IP 格式
  3. 把正则用进 sed -E 批量替换、awk 条件过滤、grep -o 精准摘取三类实战场景

为什么要学

日志和配置都是文本,而「按样子找内容」就是正则的主场。排查 502 要按时间窗过滤、统计异常要按模式计数、脱敏日志要批量替换手机号——没有正则,这些活全得靠人眼。Java 后端也逃不开它:String.matches、Pattern 类、Nginx location 匹配、IDE 全局替换,底层同一套语法。面试聊到「怎么从日志里捞特定模式的行」,正则是绕不过的基本功。

人话:正则是「文本的通配符进阶版」。通配符 *.log 只会匹配文件名,正则能描述「一个以 2026 开头、跟着日期、中间出现 ERROR 的行」这种复杂形状——它是描述文本形状的语言。

先建立图像

两套方言对照表(BRE = grep 默认,ERE = grep -E / sed -E / awk):

功能BRE(基础)ERE(扩展)含义
任意单字符..除换行外任意字符
零或多次**重复前一项 ≥0 次
一或多次++重复 ≥1 次
零或一次??可有可无
或||左边或右边
分组( )( )括起来当整体
n 到 m 次{n,m}{n,m}精确控制次数
行首/行尾^ $^ $锚定位置

结论:ERE 少打一半转义符。日常一律 grep -E,BRE 只在维护老脚本时能读懂即可。

元字符三兄弟的直觉:

^      行首锚定        ^ERROR    → 只匹配行首的 ERROR
$      行尾锚定        done$     → 只匹配行尾的 done
.      任意一个字符    a.c       → abc aXc a0c 都行
*      前面重复0+      ab*c      → ac abc abbc
+      前面重复1+      ab+c      → abc abbc(不含 ac)
?      前面0或1次      ab?c      → ac 或 abc
[0-9]  集合任选        [0-9]{3}  → 恰好三位数字
[^0-9] 集合取反        非 数字字符

跟着做

步骤 1:准备练习语料

mkdir -p ~/re-lab && cd ~/re-lab
cat > demo.log << 'EOF'
2026-10-06 09:00:01 INFO  request from 192.168.1.10 cost 12ms
2026-10-06 09:00:05 ERROR db connect timeout from 192.168.1.11
2026-10-06 09:00:09 WARN  slow query 3000ms
2026-10-06 09:01:12 INFO  request from 10.0.0.5 cost 8ms
2026-10-06 09:02:33 ERROR null pointer at OrderService.java:88
EOF

步骤 2:锚定与集合

grep -E "^2026" demo.log            # 以日期开头的行(全部)
grep -E "java:88$" demo.log         # 以 java:88 结尾的行
grep -E "^2026-10-06 09:0[01]" demo.log   # 09:00 或 09:01 分的行
grep -E "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" demo.log   # IPv4 形状

. 要匹配「点本身」必须转义成 \.,否则它匹配任意字符——不转义时 192.168.1.10 连 192x168 1y10 都能中。

步骤 3:分组、或、引用

grep -E "ERROR|WARN" demo.log           # 错误或警告
grep -E "(timeout|pointer)" demo.log    # 分组内二选一
grep -E "cost [0-9]+ms" demo.log        # cost 后跟数字加 ms

# 用分组提取内容:sed 的 \1 引用第 1 个分组
sed -E 's/.*from ([0-9.]+).*/IP: \1/' demo.log
# 输出 IP: 192.168.1.10 等行——从行里抠出 IP

分组引用 分组引用 \1 \2 是 sed 替换的灵魂:把想要的片段括起来,替换时按序号摆位置。

步骤 4:grep -o 摘取片段 + 计数

grep -oE "ERROR|WARN" demo.log | sort | uniq -c
#   1 WARN
#   2 ERROR        ← 不用看整行,直接数模式出现次数

grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" demo.log   # 摘出所有 IP

([0-9]{1,3}\.){3} 是分组 + 次数的组合拳:「三位数加点」重复 3 次,再接一段三位数。这就是面试让你现场写 IP 正则的答案骨架(教学简化版,严格校验需排除 256+,生产够用)。

步骤 5:sed -E 实战:脱敏与格式转换

# 日志脱敏:手机号打码
echo "user 13812345678 login" | sed -E 's/(1[3-9])[0-9]{9}/\1*********/'
# 输出:user 13********* login

# 逗号分隔转制表符
echo "a,b,c" | sed -E 's/,/\t/g'

# 时间格式统一:2026-10-06 → 2026/10/06
sed -E 's/2026-([0-9]{2})-([0-9]{2})/2026\/\1\/\2/' demo.log

生产红线:对正式文件 sed -i 之前,先不带 -i 跑一遍看输出,确认无误伤再落盘。

步骤 6:awk 按正则选行选列

awk '$0 ~ /ERROR/' demo.log            # 整行匹配正则(~ 是匹配运算符)
awk '$0 !~ /INFO/' demo.log            # 不匹配 INFO 的行
awk '/ERROR/ {print $NF}' demo.log     # 含 ERROR 的行的最后一列
awk -F: '/OrderService/ {print $2}' demo.log   # 冒号分隔取第 2 列

$0 ~ /正则/ 把正则当条件、把列当输出对象,awk 是「正则 + 字段处理」的二合一工具。

步骤 7:校验类正则(面试手写高频)

echo "13812345678" | grep -qE "^1[3-9][0-9]{9}$" && echo "手机号合法"
echo "10.0.0.5"    | grep -qE "^([0-9]{1,3}\.){3}[0-9]{1,3}$" && echo "IP 格式合法"
echo "2026-10-06"  | grep -qE "^[0-9]{4}-[0-9]{2}-[0-9]{2}$" && echo "日期格式合法"

^ 和 $ 锚定首尾做「全匹配」,少一个都可能让 xx13812345678yy 混进来。grep -q 静默模式只返回退出码,专配 if 使用。

讲清楚要点

  1. grep 默认 BRE,-E 开 ERE,-o 只输出匹配片段:-o 把「整行命中」变成「片段摘取」,配 sort/uniq 就是模式计数器。-P(PCRE,部分系统需安装)才有懒惰匹配等高级功能,优先用 ERE 解决。
  2. 正则是贪婪的:.*from 会一直吃到行内最后一个 from,不是第一个。想停在第一个匹配,用排除法:[^ ]*from 或缩小集合。Java 正则里的 ?(懒惰量词)在 ERE 里没有对应物。
  3. .、*、[、] 等元字符要匹配字面量必须转义:日志里最常见的误伤是拿 . 匹配 IP 和文件名。匹配「字面点」永远写 \.。
  4. 锚定符 ^ $ 让匹配从「包含」升级为「全等」:校验类场景(手机号、日期)必加;过滤类场景(捞 ERROR 行)反而常常不需要。
  5. sed 分组引用从 \1 开始按左括号顺序编号:s/(a)(b)/\2\1/ 实现 ab→ba 倒序。分组嵌套时数左括号:第 2 个 ( 对应 \2。
  6. 先测试后落盘是正则操作的生命线:grep 预览 → sed 不带 -i 预览 → 加 -i.bak 落盘。正则的杀伤力和它的便利性成正比。

你可能会懵的地方

  • 为什么我写的 [0-9]+ 在 grep 里报错或没效果? grep 默认 BRE,+ 是普通字符。加 -E 用 ERE,或 BRE 写法 \+。同理 |、()、{n,m} 在 BRE 里都要加反斜杠。

  • * 和 + 到底差在哪? * 允许出现 0 次(可空),+ 要求至少 1 次。ab*c 能匹配 ac,ab+c 不能。写正则时先问自己「空的情况要不要」。

  • 正则里的空格有意义吗? 有,空格就是普通字符。「502」会匹配 1502,加空格 502 才是独立字段。要么加空格,要么用 [^0-9]502[^0-9] 边界思路,要么换 awk 按列精确比对。

  • grep -o 和 sed -E 's/.../\1/' 都能提取,怎么选? 只要「片段本身」用 grep -oE,一条命令搞定;要把片段重新组织(加前后缀、调整顺序、多分组重排)用 sed 分组引用。awk 则胜在还能做统计计算。

  • 正则能校验邮箱、URL 吗? 理论能,工程上不建议追求完美——RFC 邮箱正则长到离谱。运维场景用「够用的近似」:^[^@]+@[^@]+\.[^@]+$ 验邮箱形状,配合业务层二次校验。Java 里同理。

错法对照

错法问题正解
grep "192.168.1.1" 匹配 IP. 匹配任意字符,误伤grep -E "192\.168\.1\.1"
`grep "ERRORWARN"` 不加 -E| 在 BRE 是普通字符
sed 's/(a)/\1/' 未开 -EBRE 里括号无分组含义sed -E 's/(a)/\1/'
校验手机号漏了 ^ $子串命中即通过,如 9913812345678x首尾锚定 ^1[3-9][0-9]{9}$
.* 到处用导致匹配过宽贪婪吃到行尾,跨过目标用 [^x]*、具体集合或 -o 缩小

卡点急救

  1. 正则「应该匹配却没匹配」:三步排查——① 引号问题,模式用单引号防 Shell 抢解释;② 方言问题,确认 grep 加没加 -E;③ 数据问题,cat -A 文件 看有没有隐藏的 ^M(Windows 换行)和行尾空格。
  2. sed 替换后的结果和预期不符:先去掉 -i 在屏幕预览;贪婪匹配问题改用排除字符类;特殊字符(/、&、\)转义,或换分隔符 s#a#b#g。
  3. 不同机器上同一正则行为不一致:grep 版本差异(busybox 精简版功能少)、别名带了 --color 或 -P。用 type grep 查看是否别名,脚本里用 command grep 或全路径。

面试常问

  • 写一个匹配 IPv4 的正则。 参考答案:^([0-9]{1,3}\.){3}[0-9]{1,3}$,进阶加分点:指出它放行 999.999.999.999,严格校验每段 0-255 要展开成 (25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9]) 并说明工程上常用「形状校验 + 业务校验」分层处理。

  • BRE 和 ERE 的区别? 字符集与锚定一致,差异在量词和分组:ERE 中 +、?、|、()、{n,m} 直接写,BRE 中要加反斜杠转义(+、?、|、( )、&#123;n,m&#125;)。grep 默认 BRE,-E 切 ERE;sed 默认 BRE,-E 切 ERE;awk 原生就是 ERE。

  • 怎么把日志里的手机号全部脱敏? sed -E 's/(1[3-9])[0-9]{9}/\1*********/g' app.log。要点:分组保留前两位、g 全局替换、先预览再 -i 落盘。追问合规场景可补「落盘前用 -i.bak 备份、脱敏后核对行数一致」。

自测三问

  1. [0-9]{3}、[0-9]{3,}、[0-9]{3,5} 分别匹配什么?
  2. 写出「以 2026-10 开头且包含 ERROR」的 grep -E 命令。
  3. sed 分组引用里 \2 代表什么?

小结一句话

正则是描述文本形状的语言:锚定 ^$ 定位置、集合 [] 选字符、量词 * + ? {} 控次数、分组 () 配引用做提取——用 ERE 风格,先预览后落盘。

延伸

  • GNU Grep 正则文档
  • 下一课:进程与定时任务——管理 Java 进程、理解信号,让 crontab 替你值夜班。
加载本节练习…
← 上一课函数与脚本结构下一课 →进程与定时任务