4. 正则表达式入门:常用元字符、量词、分组与捕获,在日志过滤中的实战用法
正则表达式这东西,说白了就是一把“模式匹配的手术刀”。做日志分析时,我们每天要处理海量的 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 飙高。我的经验是:尽量少用 .*,改用 [^ ]* 或者更精确的字符类。
好了,元字符、量词、分组捕获这三个工具,加上一点实战练习,已经够你在多数日志过滤场景里写出靠谱的规则了。下次碰到看不懂的正则,记得先圈出元字符,再看量词,最后看括号——基本就没有解不开的谜团。