该项目位置 --> 链接
项目核心代码基于 TTP --> 链接
TTP _start_ 和 _end_ 用法总结
基于实验验证,非文档推断。
_start_
核心规则
group 的第一行模板永远是触发行,不需要 _start_。_start_ 只有一个用途:把 group 里的非第一行也提升为触发行。
触发行的作用:命中时,保存当前记录,开启新记录。什么时候不需要 _start_
group 里只有一种触发行格式,且它就是第一行:
<group name="vpn">
ip vpn-instance {{ name }} ← 第一行,自动触发,无需 _start_
rd {{ rd }}
</group>
输入多个 ip vpn-instance 块时,每次命中第一行都会自动保存上一条、开启新记录。
什么时候需要 _start_
group 里有多种格式的触发行,且不是第一行:
<group name="interfaces">
interface Tunnel{{ id }} ← 第一行,默认触发
interface GigabitEthernet{{ id | _start_ }} ← 非第一行,必须加 _start_
interface Loopback{{ id | _start_ }} ← 非第一行,必须加 _start_
description {{ desc }}
</group>
不加 _start_ 的后果:GigabitEthernet 和 Loopback 接口完全丢失,只有 Tunnel 接口被捕获。
正确写法 vs 错误写法
结论:永远用管道符写法 {{ varname | _start_ }},不要把 {{ _start_ }} 单独写在同一行其他变量旁边。
_start_ 与触发行无关数据的写法
触发行本身没有需要捕获的数据时,_start_ 单独写:
<group name="cdp">
{{ _start_ }}------------------------- ← 分隔线,无数据,_start_ 单独写
Device ID: {{ device_id }}
IP address: {{ ip }}
</group>
_end_
TTP 的自动边界检测
不加 _end_ 时,TTP 依赖两种机制自动保存记录:
新触发行到来:先保存当前记录,再开启新记录
顶格非触发行到来:自动保存当前记录,group 回到未激活状态
interface Gi0/1 ← 触发,开始收集
ip address 10.0.0.1 ← 收集
router ospf 1 ← 顶格 + 非触发 → 自动保存 Gi0/1,group 停止收集
ip address 172.16.0.1 ← group 未激活,忽略,不会污染 Gi0/1
interface Loopback0 ← 触发,开始收集新记录
什么时候不需要 _end_
对于典型的网络设备配置文件,绝大多数情况不需要 _end_:
每个配置块都以顶格行开始(
interface、router、ip vpn-instance等)TTP 的自动边界检测能正确处理这类结构
多次实验验证:有无
_end_结果完全相同
什么时候需要 _end_
配合 _line_ 使用时,_end_ 是推荐写法。
_line_ 会匹配任意行,计算开销大。_end_ 让它提前停止扫描,减少不必要的匹配:
<group name="interfaces">
interface {{ interface }}
{{ port_sec | _line_ | contains("port-security") | joinmatches }}
!{{ _end_ }} ← 告诉 _line_:到 ! 就停,不要继续扫描后面的内容
</group>
这里 _end_ 的作用是性能优化,不是正确性保障。
多种结束条件
结束行有多种格式时,写多行 _end_,任意一行命中都触发保存:
<group name="interfaces">
interface {{ interface }}
ip address {{ ip }} {{ mask }}
!{{ _end_ }} ← IOS 风格
quit{{ _end_ }} ← 华为风格
return{{ _end_ }} ← 华为风格
</group>
对比总结
一句话判断
需要 _start_ 的判断: 这行是不是 group 的第一行?不是 → 加 _start_。
需要 _end_ 的判断: 这个 group 里有没有用 _line_?有 → 加 _end_。其他情况 TTP 自动处理。