unique
等函数结构实际上是强加的,所以我必须遵守它。一个对象"路径"通常是这样的:.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:"&# 34;}。预期的输出应为:
[
{
"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"
}
]
}
]
}
}
}
}
]
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)'
.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)'
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)'
有没有办法简化所有这些,让它更通用地处理任意数量的父,数组或不?
谢谢。
答案 0 :(得分:1)
回答问题:是的。 jq 1.5有keys_unsorted
,因此您可以使用walk/1
的以下def,它现在是jq的“主”版本中的标准:
# Apply f to composite entities recursively, and to atoms
def walk(f):
. as $in
| if type == "object" then
reduce keys_unsorted[] as $key
( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f
elif type == "array" then map( walk(f) ) | f
else f
end;
有关更多详细信息和示例,请参阅jq手册的“开发”版本,jq FAQ https://github.com/stedolan/jq/wiki/FAQ等。
使用index/1
很容易实现;您可能希望使用辅助函数,例如:
def ensure_has($x): if index([$x]) then . else . + [$x] end;
如果我正确理解了这个要求,你就会知道jq会根据作业创建对象,例如
。{} | .a.b.c = 1
产量
{"a":{"b":{"c":1}}}
因此,使用您的示例,您可能希望在walk
中包含类似的内容:
if type == "object" and has("spec")
then (.spec.template.spec.containers? // null) as $existing
| if $existing then .spec.template.spec.containers |= ...
else .spec.template.spec.containers = ...
end
else .
end
答案 1 :(得分:0)
管理以达到一个非常好的形式:
在~/.jq
中添加了以下功能:
def arr:
if length<=0 then .[0] else .[] end;
def arr(f):
if length<=0 then
.[0]
else
.[]|select(f)
end//.[length];
def when(COND; ACTION):
if COND? // null then ACTION else . end;
# Apply f to composite entities recursively, and to atoms
def walk(f):
. as $in
| if type == "object" then
reduce keys_unsorted[] as $key
( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f
elif type == "array" then map( walk(f) ) | f
else f
end;
def updobj(f):
walk(when(type=="object"; f));
典型的过滤器如下所示:
jq -r '{name:"CHANGEME",value: "xx"} as $v |
map( when(.kind == "StatefulSet";
.spec.template.spec.containers|arr|.env|arr(.name==$v.name)) |= $v)'
结果将是所有不存在的对象都将被创建。这里的约定是对你想要成为数组的每个对象使用arr
函数,最后使用布尔条件和对象来替换匹配的对象或添加到父数组(如果不匹配)
如果您知道路径始终存在,那么您要更新的对象也是如此,walk
更优雅:
jq -r 'map(updobj(select(.name=="CHANGEME").value|="xx"))'
感谢@peak的努力并激励解决方案。