yq 的 first 操作符完全指南:数组与映射中快速定位首个匹配元素
发布时间:2026/9/14 5:08:07
分类:文化教育
浏览:1234

yq 的 first 操作符完全指南数组与映射中快速定位首个匹配元素【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq本篇技术指南深入讲解 yq 中first操作符的完整用法它不仅能在数组Array中返回第一个满足条件的元素也能在映射Map中返回第一个匹配的值既可以携带一个筛选表达式按条件匹配也可以不带任何参数直接取首元素。读完本文你将掌握first的全部调用形态、其在各类数据结构数组、映射、嵌套结构、标量、空值上的行为边界以及它在 yq 底层解析器与执行器中的实现原理。first 操作符是什么first是 yq 提供的一个取第一个操作符其核心语义在 pkg/yqlib/doc/operators/headers/first.md 中被精炼地概括为三句话Returns the first matching element in an array, or first matching value in a map.Can be given an expression to match with, otherwise will just return the first.翻译过来即数组场景返回数组中第一个匹配的元素映射场景返回映射中第一个匹配的值两种形态可以附带一个匹配表达式first(条件)也可以不带表达式直接写first后者无条件返回第一个元素。这一语义与 jq 的first过滤器一脉相承是流式筛选场景找出第一个满足条件的配置项定位第一条异常记录中最常用的工具之一。完整的示例文档位于 pkg/yqlib/doc/operators/first.md。基本用法一数组中的首个匹配元素first最常见的用法是对一个数组施加条件表达式返回第一个满足条件的元素。给定sample.yml- a: banana - a: cat - a: apple执行yq first(.a cat) sample.yml输出a: cat这里first(...)遍历数组的每个元素将元素依次代入括号内的表达式(.a cat)一旦表达式求值为真true立即把该元素整体作为结果输出不再继续向后匹配。多个匹配时返回第一个当数组中存在多个满足条件的元素时first只会返回第一个命中项后续命中项被忽略。给定- a: banana - a: cat b: firstCat - a: apple - a: cat b: secondCat执行相同的yq first(.a cat) sample.yml输出的是包含b: firstCat的那一条a: cat b: firstCat这正是first第一个语义与select等全量筛选语义的分水岭select(.a cat)会输出两条 cat而first只保留最先命中的一条。两者结合使用如first(select(...))也是常见写法。数值条件条件表达式不限于字符串比较数值比较同样适用。给定- a: 10 - a: 100 - a: 1 - a: 101执行yq first(.a 50) sample.yml输出a: 100注意输出的是a: 100而非a: 101虽然两者都大于 50但100是数组中第一个满足条件者。布尔条件给定包含布尔字段的数组- a: false - a: true b: firstTrue - a: false - a: true b: secondTrue执行yq first(.a true) sample.yml输出a: true b: firstTruenull 值处理first可以配合! null跳过空值快速定位第一个非空元素。给定- a: null - a: cat - a: apple执行yq first(.a ! null) sample.yml输出a: cat复杂组合条件条件表达式支持逻辑运算符自由组合。给定- a: dog b: 7 - a: cat b: 3 - a: apple b: 5执行yq first(.b 4 and .b 6) sample.yml输出a: apple b: 5b为 7 的 dog 超出上限、b为 3 的 cat 低于下限只有b为 5 的 apple 同时满足两个条件因此成为唯一且第一个的命中项。基本用法二映射中的首个匹配值first同样适用于映射Map它会遍历映射的值value返回第一个满足条件的值。给定x: a: banana y: a: cat z: a: apple执行yq first(.a cat) sample.yml输出a: cat映射场景同样支持数值条件。给定x: a: 10 y: a: 100 z: a: 101执行yq first(.a 50) sample.yml输出a: 100在映射场景下first的遍历顺序遵循映射键的文档顺序yq 内部使用有序映射保存键值对见 pkg/yqlib/operator_traverse_path.go 中基于github.com/elliotchance/orderedmap的实现因此第一个匹配是确定性的与 YAML 文件中键的书写顺序一致。进阶用法嵌套结构与管道first可以直接与管道|组合作用于数据结构中的任意嵌套位置。给定items: - a: banana - a: cat - a: apple执行yq .items | first(.a cat) sample.yml输出a: cat先用.items取出嵌套数组再交给first完成筛选。这种先定位集合路径、再取第一个命中项的组合是处理深层嵌套配置如服务列表、路由表、节点清单时的标准范式。更多条件形态正则与长度条件表达式本身可以是任意 yq 表达式因此可以与其他操作符任意组合。正则匹配配合字符串操作符test使用正则。给定- a: banana - a: cat - a: apple执行yq first(.a | test(^c)) sample.yml输出a: cat长度条件配合length操作符按元素长度筛选。给定- a: hi - a: hello - a: world执行yq first(.a | length 4) sample.yml输出a: hello作用于元素本身就是标量的数组当数组的元素本身是标量字符串、数字而非映射时条件表达式直接用.引用元素自身。字符串数组给定- banana - cat - apple执行yq first(. cat) sample.yml输出cat数字数组给定- 10 - 100 - 1执行yq first(. 50) sample.yml输出100无参形态直接返回第一个元素first可以不携带任何表达式直接返回集合中的第一个元素等价于无条件取首项。数组给定- 10 - 100 - 1执行yq first sample.yml输出10映射数组给定- a: 10 - a: 100执行yq first sample.yml输出a: 10这种无参形态在解析器中是一个特例绝大多数需要参数的操作符在缺少右操作数时会直接报错expects 1 arg but received none而first被显式豁免。在 pkg/yqlib/expression_parser.go 的建树逻辑中可以看到当栈中没有可用参数且操作符恰为firstOpType时会直接break跳过取参逻辑将 RHS 留空pkg/yqlib/expression_parser_test.go 中的TestParserFirstOpWithZeroArgs正是对这一特例的回归测试。边界行为无匹配、空数组、标量与 nullfirst在无命中的情况下行为统一什么都不输出空结果。理解这些边界场景对编写健壮的表达式至关重要。无匹配项给定- a: banana - a: cat - a: apple执行yq first(.a dog) sample.yml由于没有任何元素的a等于dog输出为空。空数组给定[]执行yq first(.a cat) sample.yml空数组无可遍历元素输出为空。标量节点给定hello执行yq first(. hello) sample.yml标量节点没有可展开的子元素集合输出为空。null 节点给定null执行yq first(. hello) sample.ymlnull 节点同样没有子元素输出为空。无参形态first在这些输入上表现一致对空数组、标量、null 均返回空结果这一点由 pkg/yqlib/operator_first_test.go 中三个skipDoc: true的测试场景覆盖。底层实现原理first的完整执行链路可以拆解为解析注册 → 零参特例 → 遍历匹配三步。1. 解析器注册first在词法/语法层面被注册为一个一元操作符在 pkg/yqlib/lexer_participle.go 中通过simpleOp(first, firstOpType)完成注册操作符类型定义于 pkg/yqlib/operation.govar firstOpType operationType{Type: FIRST, NumArgs: 1, Precedence: 52, Handler: firstOperator, CheckForPostTraverse: true}NumArgs: 1声明它接受一个参数条件表达式Precedence: 52定义了它在表达式树中的结合优先级CheckForPostTraverse: true表示它支持后置遍历展开。其中NumArgs与实际可零参之间的矛盾正是上一节所述解析器特例的来源。2. 零参特例处理普通一元操作符在表达式树构建时若参数不足会抛出错误而 pkg/yqlib/expression_parser.go 对firstOpType做了显式豁免当栈为空且操作符为first时不弹出任何参数直接以RHS nil继续构建。firstOperator的实现在第 12 行pkg/yqlib/operator_first.go对应处理了这种情况// If no RHS expression is provided, simply return the first entry in candidate.Content if expressionNode nil || expressionNode.RHS nil { if len(candidate.Content) 0 { results.PushBack(candidate.Content[0]) } continue }即没有表达式时直接取节点Content切片的第一项数组首元素或映射首个值集合为空则跳过。3. 带条件的遍历匹配带条件时firstOperator对每个候选节点执行如下逻辑pkg/yqlib/operator_first.go展开splat调用splat实现在 pkg/yqlib/operator_traverse_path.go底层由traverseNodesWithArrayIndices完成将数组/映射的每个子元素逐个取出作为独立候选代入求值对每个子候选单独构建上下文执行 RHS 条件表达式d.GetMatchingNodes(splatContext, expressionNode.RHS)真值判定对条件表达式的求值结果调用isTruthyNode判定是否为真pkg/yqlib/operator_booleans.go。该函数对 null 节点直接判假对布尔标量按 YAML 布尔真值y/yes/on/true不区分大小写判断其余节点一律视为真短路返回一旦某个子候选的条件为真立即将该候选元素本身而非条件表达式的求值结果压入结果集并break结束本轮遍历。值得注意的是第 4 步first返回的是满足条件的那个原始元素而不是条件表达式本身的输出。这就是为什么first(.a | test(^c))输出的是一整个{a: cat}映射而不仅是布尔值true。4. 测试用例佐证pkg/yqlib/operator_first_test.go 中的firstOperatorScenarios完整覆盖了本文前面演示的全部 17 个场景数组、映射、嵌套、无匹配、空数组、标量、null、正则、长度、标量数组、无参形态等每个场景都精确断言了输出节点在文档中的路径如D0, P[1]表示文档 0、路径索引 1与 YAML 标签如(!!map)、(!!str)、(!!int)。TestFirstOperatorScenarios同时调用documentOperatorScenarios把这些场景自动渲染进操作符文档保证文档与实现永不脱节。常见用途小结结合以上内容first的典型应用可以归纳为场景推荐写法说明数组首个匹配first(.field value)返回第一个满足条件的映射元素数组首个数值达标项first(.field N)数值比较条件跳过 null 取首个有效项first(.field ! null)配合 null 判等映射首个匹配值first(.field value)遍历映射值嵌套集合内取首个.path.to.array \| first(...)管道组合正则匹配首个first(.field \| test(^pattern))配合 test 操作符无条件取首元素first无需参数返回首项空结果兜底结合//默认值操作符无匹配时给出降级结果最后一条值得展开由于无匹配时first输出为空实战中常将其与默认值操作符//alternative/default value配合例如yq first(.a dog) // not-found在无命中时输出兜底值避免下游管道处理空输入。相关操作符详见 pkg/yqlib/doc/operators/alternative-default-value.md。小结first是 yq 中语义直观但细节丰富的一个操作符它对数组和映射一视同仁地取第一个命中项支持字符串、数值、布尔、null、正则、长度等任意可判真的条件表达式也支持完全无参的取首项形态在无匹配、空集合、标量输入等边界场景下稳定输出空结果。从实现角度看它由 pkg/yqlib/operation.go 注册、pkg/yqlib/expression_parser.go 提供零参特例、pkg/yqlib/operator_first.go 完成展开 → 逐项求值 → 真值短路的执行链路并被 pkg/yqlib/operator_first_test.go 的 17 个场景用例严格约束。掌握first你就能在各类结构化数据中高效、确定性地定位第一个是编写健壮 yq 表达式的必备技能。【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考