如何使用jq基于多层过滤精准更新JSON中的指定DNS记录值
如何使用jq基于多层过滤精准更新JSON中的指定DNS记录值
我完全懂你现在的困扰——想用jq精准定位到某一个DNS区域下的特定A记录,只更新它的records值,但之前试的命令要么把所有相关记录都改了,要么误改了其他区域的条目,完全达不到预期效果。别慌,我们来一步步解决这个问题。
首先先明确你的原始JSON数据(方便大家对照):
[ { "dns_zone_name": "zone1", "a_records": [ { "name": "record1", "records": [ "zone1_record1_item1" ] }, { "name": "record2", "records": [ "zone1_record2_item1" ] } ] }, { "dns_zone_name": "zone2", "a_records": [ { "name": "record1", "records": [ "zone2_record1_item1" ] }, { "name": "record2", "records": [ "zone2_record2_item1" ] } ] } ]
先分析你之前命令的问题
第一个命令里的
select(.dns_zone_name="zone1" and .a_records[].name=="record1")有两个核心问题:- 用了
=而不是==做比较,=是赋值操作,这会先把所有对象的dns_zone_name改成zone1,完全偏离了过滤的初衷; .a_records[]会展开数组,导致匹配到zone1后,所有a_records里的条目都会被选中修改,所以才会出现所有记录都被更新的情况。
- 用了
第二个命令
.[].a_records |= map (select(.name=="record1").records |= ["NewRecord"])会遍历所有DNS区域,修改每个区域里名为record1的A记录,所以zone2的record1也被误改了,不符合你只修改zone1/record1的需求。
正确的jq命令写法
要实现精准定位,我们需要分层过滤:先找到目标DNS区域,再在该区域内找到目标A记录,最后更新它的records值。推荐的命令如下:
map( if .dns_zone_name == "zone1" then .a_records |= map( if .name == "record1" then .records = ["New"] else . end ) else . end )
命令逻辑解释
- 外层的
map遍历数组中的每一个DNS区域对象; - 先判断当前对象的
dns_zone_name是否为zone1,如果不是,直接保持原对象不变; - 如果是
zone1,则对它的a_records数组做内层map遍历:- 对每个A记录对象,判断
name是否为record1,是的话就把records赋值为["New"],否则保持原记录不变;
- 对每个A记录对象,判断
- 这样就只会精准修改
zone1下record1的records值,其他所有条目都不受影响。
执行后的预期结果
[ { "dns_zone_name": "zone1", "a_records": [ { "name": "record1", "records": [ "New" ] }, { "name": "record2", "records": [ "zone1_record2_item1" ] } ] }, { "dns_zone_name": "zone2", "a_records": [ { "name": "record1", "records": [ "zone2_record1_item1" ] }, { "name": "record2", "records": [ "zone2_record2_item1" ] } ] } ]
额外小提示
如果你需要把zone1和record1做成可替换的变量(方便批量处理),可以用jq的--arg参数,比如:
jq --arg zone "zone1" --arg record "record1" ' map( if .dns_zone_name == $zone then .a_records |= map( if .name == $record then .records = ["New"] else . end ) else . end )' your-dns-file.json
这样就不用每次修改命令里的字符串,直接通过参数传递目标区域和记录名即可。
备注:内容来源于stack exchange,提问作者jleg
相关产品推荐
相关产品推荐

