
yq 的 sort 与 sort_by 排序操作符从数组、Map 到自定义日期字段的完整实战指南【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq导读本文聚焦 yqportable command-line YAML/JSON/XML/CSV/TOML/HCL/properties processor的sort与sort_by(exp)两个排序操作符系统讲解其在数组、对象数组、Map 上的各种排序场景包括多字段排序、降序排序、原地排序、稳定排序、自定义日期格式排序以及 null 值排序规则。读完本文你将能熟练写出从yq sort_by(.a) sample.yml到yq .cool | sort_by(keys | .[0])的各类排序表达式并理解 yq 排序底层基于 Go 标准库sort.Stable的实现原理与类型比较规则。操作符概览sort与sort_by(exp)sort用于对数组或 Map 直接排序sort_by(exp)则按一个或多个表达式例如某个子字段的值进行排序。两者的关系在源码中非常直接sortOperator只是把sort_by的表达式替换为自身引用selfReferenceOpType后再委托给sortByOperator执行见 operator_sort.gofunc sortOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { selfExpression : ExpressionNode{Operation: Operation{OperationType: selfReferenceOpType}} expressionNode.RHS selfExpression return sortByOperator(d, context, expressionNode) }在操作符注册表中SORT_BY与SORT的优先级均为 52且都带有CheckForPostTraverse标记意味着排序结果会继续参与后续管道遍历见 operation.go。因此yq sort sample.yml整体排序等价于sort_by(.)yq sort_by(.a) sample.yml按每个元素的.a字段排序若目标节点既不是数组也不是 MapsortByOperator会直接报错node at path [...] is not an array or map (its a X)见 operator_sort.go。注意文档明确指出当前阶段 yq 的排序只处理标量字段scalar fields不支持对嵌套集合结构进行递归排序。若需按键递归排序整个文档应使用sort_keys(..)其文档位于 sort-keys.md。按字符串字段排序对对象数组按子字段的字符串值升序排序是sort_by最典型的用法。给定sample.yml- a: banana - a: cat - a: apple执行yq sort_by(.a) sample.yml输出- a: apple - a: banana - a: cat按多个字段排序sort_by接受多个表达式参数按从左到右的优先级逐级比较先按.a排序.a相等时再按.b排序。给定- a: dog - a: cat b: banana - a: cat b: apple执行yq sort_by(.a, .b) sample.yml输出- a: cat b: apple - a: cat b: banana - a: dog多字段比较在底层由sortableNodeArray.Less实现它会依次取出左右两个候选节点的CompareContext匹配结果逐对比较一旦某对比较分出高下即返回全部相等时再比较匹配节点总数数量少者在前见 operator_sort.go。因此sort_by(.a, .b)的语义是先比.a.a相同再比.b。降序排序sort 与 reverse 管道组合yq 的sort/sort_by只提供升序。要降序把排序结果通过管道交给reverse操作符即可yq sort_by(.a) | reverse sample.yml对如下输入- a: banana - a: cat - a: apple输出- a: cat - a: banana - a: applereverse的源码实现operator_reverse.go要求目标必须是序列SequenceNode否则报 not an array 错误它会把元素顺序整体倒转并保留注释。因此推荐先稳定升序、再整体反转的组合方式既保证结果可预期也避免在比较函数中实现反向逻辑带来的不稳定问题。原地排序更新模式|排序结果可以直接写回原节点用于修改文档本身。给定cool: - a: banana - a: cat - a: apple执行yq .cool | sort_by(.a) sample.yml输出cool: - a: apple - a: banana - a: cat|是 yq 的赋值更新语法表示对.cool应用右侧表达式并写回。这也是后续按复杂表达式排序示例中反复使用的基础模式。若希望直接修改磁盘上的文件可加-i参数例如yq -i .cool | sort_by(.a) sample.yml。按复杂表达式排序不止是路径sort_by的参数可以是任意合法的 yq 表达式而不仅仅是字段路径。例如按每个对象第一个键的字母序排序cool: - b: banana - a: banana - c: banana执行yq .cool | sort_by(keys | .[0]) sample.yml输出cool: - a: banana - b: banana - c: banana这里keys取出每个对象的键列表.[0]取第一个键作为排序键。测试用例中同样的思路还有sort_by(key | downcase)忽略大小写按键排序、to_entries | sort_by(.key) | reverse | from_entries先转成键值对数组再排序再转回 Map见 operator_entries_test.go。排序 Map默认按值排序sort直接作用于 Map 时默认按值values排序。给定y: b z: a x: c执行yq sort sample.yml输出键随值一起移动z: a y: b x: c排序 Map按键排序自定义函数用sort_by配合key函数可以按键排序。给定Y: b z: a x: c执行yq sort_by(key | downcase) sample.yml输出注意downcase使排序不区分大小写但保留原始键名x: c Y: b z: a从源码看sortByOperator对MappingNode的处理方式是对每个键值对计算排序键后用AddKeyValueChild(sortedNode.Node.Key, sortedNode.Node)重建 Mapoperator_sort.go因此排序后键名、值及其关联关系均保持不变。稳定排序相等元素保持原序yq 的排序是稳定的——排序键相等时元素保持原有相对顺序。给定- a: banana b: 1 - a: banana b: 2 - a: banana b: 3 - a: banana b: 4执行yq sort_by(.a) sample.yml输出与输入完全一致- a: banana b: 1 - a: banana b: 2 - a: banana b: 3 - a: banana b: 4稳定性来自源码中直接调用 Go 标准库的sort.Stable(sortableArray)operator_sort.gosortableNodeArray仅实现Len/Swap/Less三个接口方法operator_sort.go。结合多字段排序时先比a再比b的逐对比较逻辑稳定排序正是多字段排序正确性的前提。按数字字段排序数值比较而非字符串比较sort_by(.a)对数字字段按数值大小排序而不是按字符串字典序。给定- a: 10 - a: 100 - a: 1执行yq sort_by(.a) sample.yml输出- a: 1 - a: 10 - a: 100底层比较逻辑sortableNodeArray.compare见 operator_sort.go会区分节点标签两个!!int之间用parseInt64解析后比较整数与浮点!!float混合时用strconv.ParseFloat统一按 64 位浮点比较。测试中还有一个专门用例验证大整数不会因 int64 相减而发生溢出错误[5000000000000000000, -5000000000000000000]见 operator_sort_test.go。按自定义日期字段排序with_dtf 指定解析格式默认情况下yq 按 RFC3339 格式2006-01-02T15:04:05Z07:00解析!!timestamp类型的日期。若日期是自定义字符串格式需要用with_dtf指定解析模板。给定- a: 12-Jun-2011 - a: 23-Dec-2010 - a: 10-Aug-2011执行yq with_dtf(02-Jan-2006; sort_by(.a)) sample.yml输出按真实时间先后排序- a: 23-Dec-2010 - a: 12-Jun-2011 - a: 10-Aug-2011with_dtf的第一个参数是 Go time 布局字符串02-Jan-2006表示日-月缩写-四位年份第二个参数是应用该解析格式的表达式。底层实现中sortableNode会携带dateTimeLayoutcompare函数在两侧都是!!timestamp时调用parseDateTime解析比较如果左侧是!!str且当前布局不是 RFC3339还会先尝试把字符串按该布局解析成时间再比较operator_sort.go。若解析失败yq 会打印警告并退化为普通字符串比较日期解析细节见 operator_datetime.go 与 datetime.md。排序的类型优先级null 排最前混合类型数组中sort遵循固定的类型优先级null 最前其次是布尔false 在前、true 在后然后是数字最后是字符串。给定- 8 - 3 - null - 6 - true - false - cat执行yq sort sample.yml输出- null - false - true - 3 - 6 - 8 - cat该顺序直接由compare函数的分支结构决定operator_sort.gonull 与非 null 比较时 null 恒小布尔与非布尔比较时布尔恒小两个布尔之间用isTruthyNode判断真值false true。数字之间按数值比较3 6 8其余情况如字符串回退到strings.Compare字典序因此cat排在最后。若元素使用了自定义标签yq 会先通过guessTagFromCustomType猜测其基础类型再参与比较operator_sort.go。常见组合技巧小结需求表达式数组整体升序yq sort sample.yml按字段升序yq sort_by(.a) sample.yml按多字段升序yq sort_by(.a, .b) sample.yml按字段降序yq sort_by(.a) | reverse sample.yml原地排序写回yq .cool | sort_by(.a) sample.yml按复杂表达式排序yq .cool | sort_by(keys \| .[0]) sample.ymlMap 按值排序yq sort sample.ymlMap 忽略大小写按键排序yq sort_by(key \| downcase) sample.yml自定义日期格式排序yq with_dtf(02-Jan-2006; sort_by(.a)) sample.yml以上所有示例均可在 yq 的源码测试 operator_sort_test.go 中找到一一对应的表达式场景包括字符串、多字段、降序、原地、按 key、Map、稳定、数字、日期、null 等用例文档正文见 sort.md测试覆盖了全部文档示例可作为验证表达式行为的权威参照。若需递归按键排序适合 diff 两份 YAML可参考 sort-keys.md 中的sort_keys(..)用法。【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考