Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

分隔符模式(Sep Pattern)

本文档介绍 WPL 中 {...} 分隔符模式语法,用于在字段分隔位置使用通配符和特殊匹配进行灵活切分。

适用于快捷分隔符(\,\s 等)无法满足的复杂场景,例如“跳过任意内容直到关键字““空白 + 特定前缀“等。

提示:如果你的分隔需求可以用固定字符串满足,优先使用快捷分隔符(参考 WPL 基础 - 分隔符优先级与合并)。


📚 文档导航

主题内容
基本语法{...} 写法、通配符、转义字符
语法表所有支持的记法一览
通配符 *?非贪婪匹配、单字符匹配
空白匹配\s 连续空白、\h 水平空白
保留标记 ()匹配但不消费,留给下一阶段
使用约束*() 的限制
实战示例常见场景与完整规则

基本语法

在字段的分隔符位置使用花括号 {...} 包裹模式表达式:

# 快捷分隔符
chars\,

# 模式分隔符
chars{*=}

模式分隔符与快捷分隔符遵循相同的优先级规则(字段级 > 组级 > 上游)。


语法表

记法说明示例
a字面字符 a{abc} 匹配 "abc"
*零或多个任意字符(非贪婪,最短匹配){*=} 匹配到第一个 =
?恰好一个任意字符{field?:} 匹配 "fieldA:"
\0空字节
\n换行
\t制表符
\r回车
\s一个或多个连续空白 [ \t\r\n]+{\s=} 跳过空白后匹配 =
\h一个或多个水平空白 [ \t]+{\h:\h} 匹配 " : "
\\ \* \? \{ \} \( \)字面转义{\*} 匹配字面 *
(...)匹配但不消费(仅限模式末尾){*(key=)} 保留 key=

注意:\s\h{} 模式内部匹配一个或多个连续空白;{} 之外的 \s 沿用原有语义,旧配置不受影响。


通配符 *?

* — 非贪婪匹配

* 匹配零或多个任意字符,采用最短匹配策略。

输入:a=b=c
模式:{*=}
匹配:a=          ← 找到第一个 "=" 即停止,而非 "a=b="

典型场景: 消费到某个关键字符。

# 匹配到第一个等号
chars{*=}

# 匹配到第一个冒号加空格
chars{*:\s}

? — 单字符匹配

? 恰好匹配一个任意字符。

输入:field1: value
模式:{field?:\s}
匹配:field1:         ← "?" 匹配了 "1"

约束: 模式中 * 最多出现一次,超过则报配置错误。? 无数量限制。


空白匹配 \s\h

\s — 连续空白

匹配一个或多个空白字符(空格、制表符、回车、换行)。

输入:key   =value
模式:{\s=}
匹配:   =             ← 三个空格 + "="

\h — 水平空白

匹配一个或多个水平空白字符(仅空格和制表符,不含换行)。

输入:name    :    value
模式:{\h:\h}
匹配:    :             ← 制表/空格 + ":" + 制表/空格

\S\H

与上面相反:

记法说明
\S一个或多个连续非空白字符
\H一个或多个连续非水平空白字符

保留标记(Preserve)

() 包裹的内容参与匹配以确认分隔位置,但不会从输入流中消费。下一阶段从 () 内容的起始处继续读取。

输入:hello  key=value
模式:{*\s(key=)}

消费:hello            ← 被截断,作为当前字段的值
保留:key=value        ← 留在输入流中,下一个字段从这里开始

使用方式

# 消费到 "command=" 之前,保留 "command=" 给下一个字段
chars{*(command=)}

# 消费任意内容 + 空白区域,保留 "next" 给下一个字段
chars{*\s(next)}

约束

  • () 只能出现在模式末尾{*(key=)} 合法,{(key)*=} 不合法。
  • () 不允许嵌套。
  • () 内允许字面量、\s \h \0 \n \t \r? 及转义字符。
  • ()不允许 *——保留部分必须是确定长度。

使用约束

约束说明
* 最多一个整个模式中 * 最多出现一次,多于一个报错
() 仅末尾保留标记只能出现在模式的最后
() 内无 *保留段不允许不确定长度的通配
() 无嵌套不允许 ((...))
不可与 ups_val 混用同时配置 {} 与次级结束符时,解析阶段报错

实战示例

场景 1:Key=Value 风格日志

日志格式中,字段以 key=value 排列,每个 value 后面跟空白和下一个 key。

输入:src=192.168.1.1 dst=10.0.0.1 action=accept proto=TCP
rule kv_log {
  (
    ip{*\s(dst=)}:src,
    ip{*\s(action=)}:dst,
    chars{*\s(proto=)}:action,
    chars:proto
  )
}
# 输出:src=192.168.1.1, dst=10.0.0.1, action=accept, proto=TCP

说明:每个字段使用 {*\s(next_key=)} 消费到下一个 key 之前的空白,同时保留下一个 key 给后续字段。

场景 2:分隔符中包含空白

日志中字段之间用 |(空格 + 竖线 + 空格)分隔。

输入:192.168.1.1 | admin | 2024-01-01 10:00:00
rule pipe_sep {
  (ip:client, chars:user, time:ts){\h|\h}
}
# 输出:client=192.168.1.1, user=admin, ts=2024-01-01 10:00:00

场景 3:匹配到特定关键字

消费到 command= 出现的位置。

输入:user=admin role=root command=ls -la
rule cmd_log {
  (
    chars{*(command=)}:prefix,
    chars:command
  )
}
# prefix = "user=admin role=root "
# command = "ls -la"(前面 "command=" 被 consume_sep 消费)

场景 4:字段名后跟变化字符

某些日志的字段名格式为 fieldN:,N 是一个可变字符。

输入:field1: hello field2: world
rule field_var {
  (chars{field?:\s}:f1, chars:f2)
}
# f1 = "hello", f2 = "world"

场景 5:纯字面量模式

{} 内不含通配符时,等价于快捷字面量分隔符,但可以表达多字符序列。

# 以下两种写法效果相同
chars{::}
# 等价于
chars\:\:

场景 6:结合管道函数

模式分隔符可与字段级管道组合使用。

rule combined {
  (
    chars{*\s(src=)}:header,
    kvarr(\s):payload
  ) |(take src)
}

与快捷分隔符的对比

特性快捷分隔符模式分隔符
语法\, \; \s{...}
匹配能力固定字符串通配符、空白区域、保留标记
性能最优(memchr)接近(纯字面退化为 memchr)
适用场景分隔符是确定字符分隔逻辑含变化部分
优先级与字段/组级一致与字段/组级一致

选择建议:

  • 分隔符是固定字符(逗号、空格、竖线等)→ 使用快捷分隔符
  • 分隔符包含空白区域、需要跳过到关键字、需要保留部分内容 → 使用模式分隔符

常见问题

Q: {...} 内的 \s 和外面的 \s 一样吗?

不完全一样。{} 内的 \s 匹配一个或多个连续空白字符;{} 外面的 \s 沿用原有语义(表示空格分隔符)。旧配置不受影响。

Q: 可以在组级使用模式分隔符吗?

可以。与快捷分隔符一样,模式分隔符支持字段级和组级:

# 字段级
(chars{*=}, digit)

# 组级
(chars, digit){*=}

Q: * 匹配到行尾怎么办?

如果 * 后续没有其他匹配内容,* 会匹配到输入的末尾。如果没找到后续内容的匹配,则该模式整体不匹配,字段将读取到行尾。

Q: 可以只用 () 不带 * 吗?

可以。() 不依赖 *,例如 {\s(key=)} 表示:匹配空白区域后确认 key= 存在,消费空白部分,保留 key=


相关资源