正则表达式入门:基本元字符、量词、分组与反向引用

从一次日志排查开始

我记得刚接触HSM日志解析那会儿,面对几千行的原始日志,用grep匹配总是差那么一点。后来才明白,正则表达式就像一把精确的手术刀,能帮你从文本中准确切出想要的部分。

这一节咱们就聊聊正则的三大块:基本元字符、量词、分组与反向引用。说白了,掌握了它们,你写脚本解析HSM日志时会顺手很多。

基本元字符:字符的定位棋

正则最基本的元素就是元字符。每个元字符代表一类字符或一个特殊位置。

元字符含义例子匹配示例
.匹配任意单个字符(除换行)a.baab, a1b, a_b
^匹配行首^ErrorError at line...
$匹配行尾done$task done
\d匹配一个数字 [0-9]\d{3}123
\w匹配字母、数字、下划线\w+hsmlog2023
\s匹配空白符(空格/tab/换行)\s+

实践中我发现,刚开始容易把 . 当作“什么都行”,但要注意它默认不匹配换行。我在项目中就踩过这个坑——解析多行日志时,.* 死活不跨行,后来加了 s 修饰符才搞定。所以记得灵活用 \s\S 组合来跨行匹配。

量词:控制重复次数

光有元字符还不够,我们还要说“出现几次”。量词就是干这个的。

量词含义例子匹配
*零次或多次ab*a, ab, abb
+一次或多次ab+ab, abb(不匹配a)
?零次或一次ab?a, ab
{n}恰好n次\d{4}2023
{n,}至少n次\d{2,}12, 123, 4567
{n,m}n到m次\d{2,4}12, 123, 1234

大家要注意贪婪与懒惰匹配的区别。默认是贪婪的——匹配尽可能多的字符。比如用 .* 匹配 "abc"def" 时,会匹配到最后一个引号。如果你想要非贪婪,加个问号变成 .*?,它就匹配到第一个引号就停了。我写日志解析脚本时经常用非贪婪,避免吃进太多无关内容。

分组:把模式打包

圆括号 () 有两个作用:分组和捕获。分组可以让你对一组字符应用量词,比如 (ab)+ 匹配 ababab。更重要的是,分组会捕获匹配的内容,供后续引用。

举个例子,HSM日志中经常有 Session ID: 12345 这样的行。我们可以写:

Session ID: (\d+)

这样括号里的数字就被捕获到了第1组。如果你用shell的 grep -P 或 sed,就可以用 \1 引用它。

实用技巧: 在 sed 替换时,sed -E 's/(\d{4})-(\d{2})-(\d{2})/\3.\2.\1/' 可以把日期格式从 YYYY-MM-DD 变成 DD.MM.YYYY。我经常用这个批量改日志时间戳。

反向引用:复用已捕获的内容

反向引用允许你在同一个正则表达式中引用之前捕获的分组。比如你要匹配成对的标签:

<([a-z]+)>.+</\1>

这个模式能确保开始标签和结束标签名称一致,比如匹配 <error>something</error>,而不会匹配 <error></warning>。

注意: 反向引用在不同工具中语法略有差别。在 sed 中用 \1,在 Perl 兼容正则中用 \1 或 $1(替换时)。写shell脚本时,我习惯用 awk 的 match() 配合数组,更清晰。

综合示例:从HSM日志提取关键字段

假设我们有这样一行日志:

2024-01-15 14:32:18,INFO,HSM_Module,KeyGen,Success,Session=abc123,KeyID=0x4F2A

我们希望提取日期、级别、操作和KeyID。正则可以写成:

^(\d{4}-\d{2}-\d{2})\s+\d{2}:\d{2}:\d{2},(INFO|WARN|ERROR),.*?,.*?,(Success|Failure),Session=\w+,KeyID=(0x[0-9A-F]+)$

用 grep -Po 加上分组引用就能精确定位。我个人习惯把这种正则存成函数库,省的每次重写。

知识结构总览

下面这张图帮你梳理正则三大核心的关系:

正则表达式核心知识结构 基本元字符 量词 分组与反向引用 ^ 行首 $ 行尾 . 任意字符 \d \w \s * + ? {n,m} 贪婪 vs 懒惰 分组( ) 反向引用 \1 三者组合使用,可解析复杂日志结构

核心总结: 正则不是背出来的,是用出来的。你只要记住三类元素:元字符定位字、量词控次数、分组抓内容。上手先从小模式开始,慢慢拼合。我在写HSM日志解析时,经常把复杂的正则拆成三四步,每步测试一行,最后再合并。这样做不容易出错,也方便调试。

好了,这一节的内容就到这儿。记得多用shell命令 grep -P、sed -E 和 awk 练手,把今天学的元字符、量词、分组用起来。