在以下 json 文件中,
{
"contacts": [
{
"name": "John",
"phone": "1234"
},
{
"name": "Jane",
"phone": "5678"
}
]
}
我需要根据姓名更新两个电话号码,并将整个 json 存储在一个新文件中。
我尝试过类似的东西:
jq '.contacts[] | select(.name == "John") | .phone = "4321"' < contacts.json >updated_contacts.json
但随后我不知道如何返回父节点并更改 Jane 的节点,也不知道如何检索整个 json。
我尝试使用 存储到变量中的根节点as
,但它保持不变。
作为临时解决方法,我只是这样做:
jq '.contacts[0].number = "4321" | .contacts[1].number = "4321"' < contacts.json >updated_contacts.json
但是我不应该依赖数组索引,而是名称,因为原始 json 可能会改变。
知道如何使用 jq 命令来做到这一点吗?
答案1
要更改一个条目,请确保您使用|=
且该更新运算符的左侧是原来的文档:
jq --arg name John --arg phone 4321 \
'( .contacts[] | select(.name == $name) ).phone |= $phone' file
您不能使用,.contacts[] | select(.name == "John") | .phone |= ...
因为select()
实际上会从数组中提取一组元素contacts
。因此,您只能更改提取的元素,与文档的主要部分无关。
注意其中的差异
( ... | select(...) ).phone |= ...
^^^^^^^^^^^^^^^^^^^^^
path in original document
哪个有效,并且
... | select(...) | .phone |= ...
^^^^^^^^^^^
extracted bits
这是行不通的。
对多个条目使用循环,假设例如bash
:
names=( John Jane )
phones=( 4321 4321 )
tmpfile=$(mktemp)
for i in "${!names[@]}"; do
name=${names[i]}
phone=${phones[i]}
jq --arg name "$name" --arg phone "$phone" \
'( .contacts[] | select(.name == $name) ).phone |= $phone' file >"$tmpfile"
mv -- "$tmpfile" file
done
也就是说,我将名称放入一个数组中,将新数字放入另一个数组中,然后循环索引并file
使用临时文件和中间存储更新每个需要更改的条目。
或者,使用关联数组:
declare -A lookup
lookup=( [John]=4321 [Jane]=4321 )
for name in "${!lookup[@]}"; do
phone=${lookup[$name]}
# jq as above
done
假设您有一些包含新电话号码的 JSON 输入文档,例如
{
"John": 1234,
"Jane": 5678
}
您可以使用它来创建
jo John=1234 Jane=5678
jq
然后您可以在一次调用中更新数字:
jo John=1234 Jane=5678 |
jq --slurpfile new /dev/stdin \
'.contacts |= map(.phone = ($new[][.name] // .phone))' file
这会读取我们的输入 JSON,并将新数字放入一个结构中,$new
看起来像
[
{
"John": 1234,
"Jane": 5678
}
]
这在呼叫中用于map()
更改列出的任何联系人的电话号码。确保// .phone
即使未列出姓名,电话号码也保持不变。
答案2
根据 Kusalananda 的回答,如果您只想搜索和替换 2 个值,您可以在一次 jq 调用中执行如下操作:
jq '( .contacts[] | select(.name == "John") ).phone |= "4321" |
( .contacts[] | select(.name == "Jane") ).phone |= "8765"' \
contacts.json
或者以这种方式链接 2 个 jq 调用:
cat contacts.json | \
jq '( .contacts[] | select(.name == "John") ).phone |= "4321"' | \
jq '( .contacts[] | select(.name == "Jane") ).phone |= "8765"'