ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

yq 等值判断操作符完全指南:`==` / `!=` 的原理、通配匹配与实战用法

yq 等值判断操作符完全指南:`==` / `!=` 的原理、通配匹配与实战用法 yq 等值判断操作符完全指南/!的原理、通配匹配与实战用法【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq与!是 yqpkg/yqlib中最基础也最常用的布尔操作符用于判断表达式左右两侧LHS/RHS的节点是否相等。本文以官方文档 pkg/yqlib/doc/operators/equals.md 为核心结合operator_equals.go、matchKeyString.go、operators.go等源码实现与测试用例系统讲解二者的行为语义、与select的配合方式、字符串通配匹配、数字与 null 匹配以及键不存在这类边界场景的处理逻辑。读完本文你将能精确预测任意/!表达式的输出并在数据筛选、配置校验等场景中写出正确的过滤表达式。操作符基本语义是一个布尔操作符boolean operator当 LHS左侧表达式与 RHS右侧表达式相等时返回true否则返回false。与之对应!在 LHS 与 RHS 相等时返回false否则返回true——它正是的取反。最简单的用法是直接比较两个字段yq .a .b sample.yml该表达式会基于文档根节点对.a与.b分别求值并比较。从源码看与!在 pkg/yqlib/operation.go 中注册为var equalsOpType operationType{Type: EQUALS, NumArgs: 2, Precedence: 40, Handler: equalsOperator} var notEqualsOpType operationType{Type: NOT_EQUALS, NumArgs: 2, Precedence: 40, Handler: notEqualsOperator}可以看到二者都是二元操作符NumArgs: 2优先级Precedence同为 40且!在词法层面由 pkg/yqlib/lexer_participle.go 中的正则\s*!\s*解析为notEqualsOpTypetoken。实现上equalsOperator与notEqualsOperatorpkg/yqlib/operator_equals.go共用同一个比较函数isEquals区别仅在于notEqualsOperator传入fliptrue在得到相等性结果后取反func equalsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { return crossFunction(d, context, expressionNode, isEquals(false), true) } func notEqualsOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { return crossFunction(d, context.ReadOnlyClone(), expressionNode, isEquals(true), true) }二者都经由crossFunctionpkg/yqlib/operators.go完成LHS 求值 → 对每个 LHS 节点求 RHS → 两两比较的交叉求值流程。与 select 组合最常用的过滤场景/!最常见的实战形态是与select操作符配合从集合中筛选出满足条件的节点select(.a .b)select会保留使括号内条件为真的节点丢弃为假的节点。由于/!返回布尔值它们天然是select的理想条件表达式。相关的select操作符详见 select 文档布尔组合操作符and、or、not、any、all详见 boolean-operators 文档大小比较、等详见 compare 文档。例如结合布尔操作符进行多条件过滤yq [.[] | select(.a cat or .b dog)] sample.yml在 operator_booleans.go 中isTruthyNode定义了节点如何被当作布尔值null为假字符串y/yes/on/true不区分大小写为真其余标量与复合节点为真。select正是依据该函数判断条件节点是否成立。字符串匹配与通配符/!比较字符串标量时支持 glob 通配匹配。给定sample.yml- cat - goat - dog执行yq .[] | (. *at) sample.yml输出true true falsecat与goat均以at结尾故匹配*atdog不匹配。.[]遍历数组每个元素逐个与*at比较。对应的不匹配写法yq .[] | (. ! *at) sample.yml输出false false true这里的*是通配符而非正则。底层实现在 pkg/yqlib/matchKeyString.go 中matchKey首先对空模式与*做特判空模式退化为精确相等*直接匹配一切随后调用deepMatch进行线性时间 glob 匹配func matchKey(name string, pattern string) (matched bool) { if pattern { return name pattern } if pattern * { log.Debug(wild!) return true } return deepMatch(name, pattern) }deepMatch源自 research.swtch.com/glob 的经典算法支持两种通配符*匹配零个或多个任意字符?匹配单个任意字符。该函数通过记录星号回溯点nextPx/nextNx实现线性时间匹配而非回溯式的指数复杂度。需要说明的是这一通配匹配逻辑仅作用于字符串标量的相等比较并非 YAML 锚点/别名展开后的结构比较。数字匹配/!同样支持数字比较。给定sample.yml- 3 - 4 - 5执行yq .[] | (. 4) sample.yml输出false true false仅值为4的元素匹配。不相等版本yq .[] | (. ! 4) sample.yml输出true false true数字比较走的是标量ScalarNode分支。在isEquals中当两侧均为 ScalarNode 时调用matchKey(lhs.Value, rhs.Value)即基于节点的文本表示进行比较与 operator_compare.go 中compareScalars按!!int/!!float解析数值后比较不同的相等判定并不做数字类型的强转归一这一点在涉及3与3.0这类数值相同、文本不同的节点时需留意实际行为取决于输入解析出的节点值文本与标签。null 匹配支持 null 比较。执行yq --null-input null ~输出true--null-input即-n表示不从标准输入读取数据而是直接以空文档开始求值~是 YAML 中 null 的缩写。在 operator_equals_test.go 中null null与null ~两个场景的期望输出均为true与文档一致。底层逻辑见isEquals当lhs.Tag !!null时相等性直接取决于rhs.Tag是否为!!null即两个 null 相等if lhs.Tag !!null { value (rhs.Tag !!null) }键不存在时的行为/!对不存在的键有一套独特且一致的语义理解它有助于避免写出出乎意料的过滤表达式。不存在的键不等于任何值给定sample.ymla: frog执行yq select(.b ! thing) sample.yml输出a: frog文档中不存在b键.b求值为空nil。当 LHS 为 nil 而 RHS 非 nil 时isEquals按LHS 是否等于 null处理RHS 的 tag 不是!!null故为 false!flip 后为 true——因此select(.b ! thing)条件成立整个文档被保留输出。两个不存在的键相等给定同样的sample.ymlyq select(.b .c) sample.yml输出a: frogb与c都不存在两侧均为 nilisEquals的第一个分支lhs nil rhs nil直接返回!flip即为true文档被保留。这也解释了为何select(.b .c)这类双缺失比较会成立。从源码看nil 分支的完整逻辑为pkg/yqlib/operator_equals.goif lhs nil rhs nil { owner : CandidateNode{} return createBooleanCandidate(owner, !flip), nil } else if lhs nil { value : rhs.Tag !!null if flip { value !value } return createBooleanCandidate(rhs, value), nil } else if rhs nil { value : lhs.Tag !!null if flip { value !value } return createBooleanCandidate(lhs, value), nil }即单侧为 nil 时等价于将该侧视为 null 与另一侧比较双侧为 nil 时视为两个 null 相等。测试用例 pkg/yqlib/operator_equals_test.go 完整覆盖了上述两个场景。源码级工作原理把的完整求值链路串起来可以更清楚地理解它的行为词法解析lexer_participle.go将、!识别为对应 token注册与优先级operation.go定义二者为二元操作符优先级 40分别绑定equalsOperator与notEqualsOperator交叉求值crossFunctionpkg/yqlib/operators.go先对 LHS 求值得到候选节点集合再对每个 LHS 节点求 RHS最后调用isEquals完成两两比较若 LHS 无匹配节点但当前上下文非空CalcWhenEmptytrue还会用 nil 作为 LHS 参与比较——这正是不存在的键语义的来源相等判定isEquals按顺序处理双侧 nil / 单侧 nil / LHS 为 null / 标量文本匹配四类情况最终得到一个布尔结论产出布尔节点createBooleanCandidatepkg/yqlib/operators.go以比较结果创建一个!!bool标签的标量节点作为整个表达式的输出。整个流程也被 pkg/yqlib/operator_equals_test.go 中equalsOperatorScenarios表驱动的 21 个场景所覆盖其中字符串/数字/null/缺失键等 8 个场景被标记为文档场景会同步生成到官方文档例如文档{}下.a .b输出true、select(.c ! null)过滤后输出空等均可作为理解边界行为的参考用例。实战建议与注意事项配合select完成过滤select(.field expected)是筛选配置项、按条件输出资源的最常用写法!则用于反向筛选。警惕双缺失即相等select(.b .c)在两个键都不存在时为真若想排除缺失场景应显式叠加存在性判断如与has操作符组合见 has 文档。字符串比较支持通配*/?通配只对字符串标量生效且*单独出现时匹配一切需要正则匹配时请改用其他操作符见 string-operators 文档。null 判断用~或null--null-input配合null ~可快速验证 null 语义isEquals中单侧 nil 等价于与 null 比较因此select(.b null)也可以用来挑选键缺失或值为 null的节点。与比较操作符的分工/!只回答是否相等需要大小关系时使用、、、见 compare 文档它们分别走compareScalars的数值/时间戳/字符串比较路径。如果想在本地复现文中所有示例可以创建上述sample.yml文件或直接使用仓库 examples 目录下现成的示例数据如 examples/sample.yaml再配合 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),仅供参考
返回列表