4. 正则表达式入门:常用元字符、量词、分组与捕获,在日志过滤中的实战用法

正则表达式这东西,说白了就是一把“模式匹配的手术刀”。做日志分析时,我们每天要处理海量的 HTTP 外发请求,光靠肉眼根本盯不过来。我刚开始接触日志过滤时,也觉得正则又难写又难读——直到我亲手写过几条规则之后,才发现它其实有章可循。

今天咱们就把正则的基础三件套掰开揉碎:元字符、量词、分组与捕获。学完之后,你就能在日志过滤规则里手写简单的匹配模式了。

核心观点:正则不是背语法,而是理解“我要匹配什么样的一段字符串”。把正则拆成:字符类 + 数量控制 + 位置控制 + 引用,会好记很多。

正则表达式核心结构 正则表达式核心结构 正则表达式 元字符 · . 匹配任意单个字符 · ^ 行首 / $ 行尾 · [abc] 字符集合 · | 逻辑或 · \d \w \s 简写类 量词 · * 零次或多次 · + 一次或多次 · ? 零次或一次 · {n} 精确 n 次 · {n,m} 范围次数 分组与捕获 · ( ) 定义子组 · (?: ) 非捕获分组 · \1 \2 后向引用 · 交替:| 在组内 · 嵌套分组 三者组合使用,可以写出精准的 HTTP 日志过滤模式

4.1 常用元字符——匹配的“原子”

元字符就是正则世界里最基础的字母表。我个人习惯把元字符分成三类:位置类、字符类、逻辑类。

分类元字符含义日志过滤实例
位置^匹配行首^192\.168\. 匹配内网IP开头的行
位置$匹配行尾/api/.*$ 匹配以 /api/ 开头的请求直到行尾
字符类.匹配除换行外的任意字符1\.1\.1\.1 中的点要转义
字符类[0-9]数字集合[0-9]+\.[0-9]+ 匹配版本号
字符类\d等于 [0-9]\d{1,3}\.\d{1,3} 匹配IP段
逻辑|或\.(jpg|png|gif) 匹配图片扩展名

我曾经遇到一个坑:日志里有很多 /v1/ 和 /v2/ 的路径,想用 ^/v\d 来匹配。结果误把 \d 当成了字面字符 d,过滤全废了。记住:反斜杠开头的简写类(\d、\w、\s)在大多数正则引擎里都生效,但别忘了一般需要转义光杆的点、反斜杠本身。

尤其注意:在 HTTP 日志中,IP 地址、URL 里的点 . 必须转义为 \.,否则它会匹配任意字符,导致规则误命中。我见过新人写的 192.168.1.1 结果匹配了 192X168Y1Z1 这种情况。

4.2 量词——控制“重复次数”

光是元字符只能匹配一个字符,加上量词,才能描述“重复多少次”。

量词含义贪婪/惰性示例
*0 次或多次贪婪\d* 匹配连续数字,可以没有
+1 次或多次贪婪\d+ 至少一位数字
?0 次或 1 次贪婪https? 匹配 http 或 https
{n}精确 n 次贪婪\d{4} 匹配四位年份
{n,}至少 n 次贪婪\d{2,} 至少两位数字
{n,m}n 到 m 次贪婪\d{1,3} 1~3 位数字

我再补充一点:量词默认是贪婪模式,会尽量往多了匹配。比如用 .* 去匹配 GET /api/user?id=1 HTTP/1.1,它会吃掉整个字符串。这时候可以加一个 ? 变成 .*?,让它变成惰性模式,能少匹配就少匹配。我在写 HTTP 路径提取规则时,经常用 /api/.*?/ 来取第一级子路径,而不是整个路径。

小技巧:在日志过滤规则中,如果你不确定量词会不会贪婪过度,先在测试环境用小样本跑一遍。很多开源 SIEM 工具(如 Wazuh)都提供了正则测试器,哪怕在 Excel 里用 REGEXMATCH 试也行。

4.3 分组与捕获——提取和复用

圆括号 () 干两件事:一是把多个字符当成一个整体应用量词,二是把匹配到的内容存下来便于后面引用。

捕获分组:(pattern) 会把匹配结果放进内存,之后可以通过 \1、\2 引用。举个实际例子——从日志里提取用户 ID 和动作:

/user/(\d+)/action/(\w+)

匹配 /user/12345/action/login 后,\1 就是 12345,\2 就是 login。大部分日志分析引擎(如 Splunk、Elasticsearch)支持在替换或输出中引用这些捕获组。

非捕获分组:(?:pattern) 只用来分组,不存结果。性能更好,而且不会打乱捕获组的编号。我建议:如果不需要引用组内内容,一律用 (?:)。

分组里的或(|):(?:com|cn|net)$ 匹配顶级域名。注意括号的作用域——如果写 com|cn|net$,就变成 com 或 cn 或 net$,语义完全不一样。

避坑指南:我曾经在写解析 User-Agent 的规则时,用了嵌套分组并且忘了加 ?:,结果 \1 指向的是内层分组,而不是我想要的浏览器版本号。后来才意识到捕获组编号是按左括号出现的顺序计数的——从 1 开始,从左到右。

4.4 实战:用正则过滤 HTTP 外发日志

咱们直接拿一段真实日志来练手:

192.168.1.10 - - [10/May/2025:08:12:33 +0800] "POST /api/v2/upload?file=report.pdf HTTP/1.1" 200 5321 "https://internal.example.com" "Mozilla/5.0 (Windows NT 10.0)"

目标 1:只允许内网 IP 访问内部 API
规则:^192\.168\.\d{1,3}\.\d{1,3}
如果发现源 IP 不是 192.168.x.x,就可以标记告警。

目标 2:提取请求方法 + 路径
规则:([A-Z]+) (\S+)
\1 是 POST,\2 是 /api/v2/upload?file=report.pdf。

目标 3:只允许特定文件后缀上传
规则:\.(?:pdf|docx|xlsx)$
如果匹配失败,说明用户试图上传非白名单后缀,需要拦截。

注意:正则引擎对 . 的处理在不同上下文有差异。在日志过滤规则里,如果你用 .* 去匹配整行,要小心性能问题——尤其当天量并发时,回溯可能导致 CPU 飙高。我的经验是:尽量少用 .*,改用 [^ ]* 或者更精确的字符类。

好了,元字符、量词、分组捕获这三个工具,加上一点实战练习,已经够你在多数日志过滤场景里写出靠谱的规则了。下次碰到看不懂的正则,记得先圈出元字符,再看量词,最后看括号——基本就没有解不开的谜团。