Ansible多when条件合并字典列表时merged_list未定义问题
问题描述
我现有两个字典列表,结构如下:
第一个列表(list1):
list1: [ { "_ref": "LAB1/1/4094", "vlan_id": 12, "vlan_name": "test_vlan_1", "vlan_subnet": "192.168.1.0/28", "vlan_vrf": "vrf_1" }, { "_ref": "LAB2/1/4094", "vlan_id": 12, "vlan_name": "test_vlan_1", "vlan_subnet": "192.168.2.0/28", "vlan_vrf": "vrf_1" } ]
第二个列表(list2):
list2: [ { "_ref": "LAB1/1/4094", "vlan_id": "12", "vlan_name": "test_vlan_1", "vlan_ref": "vlan/ZG5zLnZsYW4kLmNvbS5pbmZvYmxveC5kbnMudmxhbl92aWV3JElORlJBTEFCLjEuNDA5NC4xMg:LAB1/test_vlan_1/12" } ]
我最初编写的合并任务代码如下:
- name: merge lists final set_fact: merged_list: "{{ merged_list|default([]) + [ item[0] | union(item[1]) ] }}" when: item[0]._ref == item[1]._ref and item[0].vlan_id == item[1].vlan_id and item[0].vlan_name == item[1].vlan_name loop: "{{ query('nested', list1, list2) }}" - name: print results debug: var: merged_list
执行后得到如下报错输出:
TASK [print results] *********************************************************** ok: [localhost] => { "merged_list": "VARIABLE IS NOT DEFINED!" }
期望输出:list2条目与list1第一个字典条目匹配,需将二者合并,最终输出既要包含匹配后的合并条目,也要保留list1中未匹配的第二个条目,结构如下:
merged_list: [ { "_ref": "LAB1/1/4094", "vlan_id": 12, "vlan_name": "test_vlan_1", "vlan_subnet": "192.168.1.0/28", "vlan_vrf": "vrf_1", "vlan_ref": "vlan/ZG5zLnZsYW4kLmNvbS5pbmZvYmxveC5kbnMudmxhbl92aWV3JElORlJBTEFCLjEuNDA5NC4xMg:LAB1/test_vlan_1/12" }, { "_ref": "LAB2/1/4094", "vlan_id": 12, "vlan_name": "test_vlan_1", "vlan_subnet": "192.168.2.0/28", "vlan_vrf": "vrf_1" } ]
我初步判断when: item[0]._ref == item[1]._ref and item[0].vlan_id == item[1].vlan_id and item[0].vlan_name == item[1].vlan_name条件未按预期生效,请问该如何调整才能得到符合预期的merged_list结果?
问题原因
你判断的方向没错,原写法跑不通有两个硬问题:
- 类型不匹配导致条件永远不成立:list1里的
vlan_id是整数12,list2里的vlan_id是字符串"12",Ansible做相等判断时不会自动做类型转换,12 == "12"直接返回false,所以when条件一次都没满足过,set_fact任务从来没执行过,merged_list根本没被创建,最后打印自然报变量未定义。 - 逻辑本身漏了未匹配条目:就算你把类型问题修了让条件能命中,原嵌套循环的逻辑只会把两个列表匹配成功的条目加进结果,list1里LAB2那条没有对应list2匹配项的条目,根本不会被写入结果,最后输出会缺数据。
修复方案
不用嵌套循环,直接遍历list1逐行匹配合并即可,逻辑简单还不会漏数据:
- name: 初始化合并结果列表 set_fact: merged_list: [] - name: 逐行合并两个列表 set_fact: merged_list: "{{ merged_list + [ current_item | combine(matched_item | default({})) ] }}" loop: "{{ list1 }}" vars: current_item: "{{ item }}" # 匹配时把vlan_id统一转成字符串,解决类型不一致问题 matched_item: "{{ list2 | selectattr('_ref', '==', current_item._ref) | selectattr('vlan_name', '==', current_item.vlan_name) | selectattr('vlan_id', '==', current_item.vlan_id | string) | first | default({}) }}" - name: 打印合并结果 debug: var: merged_list
如果后续遇到vlan_id两边类型随机的场景,也可以把两边都转成整数比较,把matched_item定义里的判断改成selectattr('vlan_id', 'int', '==', current_item.vlan_id | int)即可,兼容性更强。
这个写法的逻辑是:
- 以list1为基准遍历,所有list1的条目都会被保留,不会丢数据
- 每处理一条list1的条目,就去list2里找符合三个匹配条件的项,匹配时统一vlan_id类型,避免类型不匹配问题
- 找到匹配项就把list2的字段合并到当前条目里,找不到就合并空字典,保留list1原条目内容
- 提前初始化merged_list为空列表,不会出现变量未定义的问题,最终输出和你期望的结构完全一致。
内容的提问来源于stack exchange,提问作者netv
相关产品推荐
相关产品推荐

