在以下 json 文件中,
{
"email": "xxx",
"pass": "yyy",
"contact": [
{
"id": 111,
"name": "AAA"
}
],
"lname": "YYY",
"name": "AAA",
"group": [
{
"name": "AAA",
"lname": "YYY",
}
],
我需要查找键“name”并将其值替换为“XXX”。哪个 jq 命令执行此操作?
答案1
jq 的赋值操作可以同时在任意多个位置执行更新,并且是针对这种情况而设计的。您可以使用
jq '(.. | .name?) |= "XXXX"'
到找到任意位置的每个名为“name”的字段,并一次性替换每个字段中的值与“XXXX”,并输出结果对象。
它使用递归下降运算符..
查找树中的每个值,然后从每个值中提取“名称”字段和.name
,抑制不匹配值的任何错误?
,然后使用“XXXX”一次性更新所有这些位置的对象更新赋值运算符|=
,并输出新对象。
无论文件结构是什么,这都可以工作,并更新各处的每个名称字段。
或者,如果文件始终具有此结构,并且它是您想要更改的那些特定“名称”字段,不仅仅是任何旧名称,您也可以将它们列出并分配给它们作为一个组:
jq '(.name, .contact[].name, .group[].name) |= "XXXX"'
这对以下内容执行相同的分配
- 顶级对象的“名称”字段;
- 的“名称”字段每一个“联系人”数组中的对象;和
- “group”数组中每个对象的“name”字段。
一气呵成。如果文件可能有,这特别有用其他名称字段位于不相关且您不想更改的地方。它只找到其中指定的三组位置并同时更新它们。
如果该值只是像这里一样的文字,那么简单的赋值与=
也可以工作并为您节省一个字符:(..|.name?)="XXXX"
- 如果您的值是基于整个顶级对象计算的,您也会需要这个。如果您想根据旧名称计算新名称,您可以需要使用|=
.如果我不确定使用什么,|=
通常在极端情况下会有更好的行为。
如果你有需要进行多次替换,你可以将它们连接在一起:
jq '(..|.name?) = "XXXX" | (..|.lname?) = "1234"'
将更新各处的“name”和“lname”字段,并输出整个更新后的对象一次。
其他一些可能有效的方法:
您也可以非常明确地说明您正在选择的内容
(..|objects|select(has("name"))).name |= "XXXX"`
它找到所有内容,然后仅找到对象,然后仅找到具有“名称”的对象,然后是这些对象上的名称字段,并执行与以前相同的更新。
如果您正在运行 jq 的开发版本(不太可能),那么
walk
功能还可以完成以下工作:walk(.name?="XXXX")
.所有其他版本都将在最新发布的版本 1.5 上运行。另一种多重更新可能是
jq '(..|select(has("name"))?) += {name: "XXXX", lname: "1234"}'
答案2
jq
根据功能使用walk
(需要最新版本):
jq 'walk(.name?="XXX")' file
如果您jq
不支持该walk
功能,只需将其定义为:
jq '
# Apply f to composite entities recursively, and to atoms
def walk(f):
. as $in
| if type == "object" then
reduce keys[] as $key
( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f
elif type == "array" then map( walk(f) ) | f
else f
end;
walk(.name?="XXX")
' file
答案3
或者,jtc
基于的解决方案:
bash $ jtc -w'<name>l+0' -u'"XXX"' your.json
{
"contact": [
{
"id": 111,
"name": "XXX"
}
],
"email": "xxx",
"group": [
{
"lname": "YYY",
"name": "XXX"
}
],
"lname": "YYY",
"name": "XXX",
"pass": "yyy"
}
bash $