【发布时间】:2018-02-27 17:29:30
【问题描述】:
使用:
- jq-1.5-1-a5b5cbe (Ubuntu 17.10)
- 编写查询:https://jqplay.org/
目标和条件:
- 将子对象值替换为具有父对象或数组的任意深度的另一个值,例如:
- 如果 .spec.template.spec.containers[n].env[n].name == "CHANGEME" 那么
- .spec.template.spec.containers[n].env[n].value = "xx"
- 其中 n >=0
- 如果 .name 的任何父级不存在,应该能够即时添加它们而不是退出并出现错误
- 输出 JSON 应至少具有与输入 JSON 相同的元素,不应丢失现有元素
- 数组元素内不允许重复,但顺序必须保留,因此不能使用
unique等函数
示例输入 JSON:
结构实际上是强加的,所以我必须服从它。对象“路径”通常类似于:.spec.template.spec.containers[0].spec.env[1].name。你也可以有 .containers[1],等等。这是高度可变的,有时某些元素可能存在与否,取决于特定 JSON 的架构定义。
[
{
"kind": "StatefulSet",
"spec": {
"serviceName": "cassandra",
"template": {
"spec": {
"containers": [
{
"name": "cassandra",
"env": [
{
"name": "CASSANDRA_SEEDS",
"value": "cassandra-0.cassandra.kong.svc.cluster.local"
},
{
"name": "CHANGEME",
"value": "K8"
}
]
}
]
}
}
}
}
]
场景
- 在保留输入结构的同时替换现有值,按预期工作:
jq -r 'map({name:"CHANGEME",value: "xx"} as $v | (.spec.template.spec.containers[].env[] | select(.name==$v.name))|=$v)'
-
假设我也想做同样的事情,只是 .env1 是对象 {name:"",value:""} 的父数组。预期的输出应该是:
[ { "kind": "StatefulSet", "spec": { "serviceName": "cassandra", "template": { "spec": { "containers": [ { "name": "cassandra", "env": [ { "name": "CASSANDRA_SEEDS", "value": "cassandra-0.cassandra.kong.svc.cluster.local" }, { "name": "CHANGEME", "value": "K8" } ], "env1": [ { "name": "CHANGEME", "value": "xx" } ] } ] } } } } ]- 为此,我尝试动态添加一个对象 env1:
-
jq -r 'map({name:"CHANGEME",value: "xx"} as $v | (.spec.template.spec.containers[] | if .env1 == null then .+={env1:[$v]} | .env1 else .env1 end | .[] | select(.name==$v.name))|=$v)'- 如果 .env1 存在则有效,否则:
- 错误:尝试访问 {"name":"cassandra","env"..的元素 "env1" 附近的路径表达式无效。
- 如果使用
.env//[$v]或.env//=.env[$v]等符号,结果相同
-
jq -r 'map({name:"CHANGEME",value: "xx"} as $v | (.spec.template.spec.containers[].env1 | .[if length<0 then 0 else length end]) |= $v)'- 如果 .env1 不存在则有效
- 如果数组 .env1 存在,则添加另一个元素,可能会复制对象
-
- 最终我设法创建了一个工作过滤器:
-
jq -r 'def defarr: if length<=0 then .[0] else .[] end; def defarr(item): if length<=0 then .[0] else foreach .[] as $item ([]; if $item.name == item then $item else empty end; .) end; map({name:"CHANGEME",value: "xx"} as $v | (.spec.template.spec | .containers1 | defarr | .env1 | defarr($v.name) ) |=$v)'- 这按预期工作,但是太长太重,必须在对象层次结构中的每个潜在数组之后添加自定义函数
-
- 为此,我尝试动态添加一个对象 env1:
问题
有什么办法可以简化这一切,让它更通用地处理任意数量的父母、数组吗?
谢谢。
【问题讨论】:
-
Vanilla JS 似乎比这更简单(以nodejs 为例)
-
我使用 jq 作为 bash 脚本的一部分来生成部署并更改 kubernetes 集群的各种配置。我更喜欢 jq,因为它是轻量级的,并且已经在主要的 Linux 版本中提供。
-
您对“foo”的规范的问题在于 .parent1.parent2[].parent3.parent4[].parent5[] 并不是真正的路径或路径规范,因为它包含 @ 987654333@。此类表达式很有用,因为它们可用于检索多个值,但如果您将路径视为仅由字符串和整数组成的数组,它可能会有所帮助。这样的数组实际上被 jq 的
getpath和setpath使用,并且有充分的理由。 -
明白,我实际上会删除那个幻想规格