Ansible循环set_fact追加列表仅保留最后一项值如何解决
Ansible循环拼接多YAML文件列表仅保留最后值的解决方法
问题场景
需求为拼接2个及以上YAML文件中的列表内容,示例文件:
- file1.yml
test: - start1 - begin1 - middle1 - end1
- file2.yml
test: - start2 - begin2 - middle2 - end2
目标是调用role时可传入所有文件内test列表的全部值。
异常表现
初始实现代码:
- name: 拼接test列表 set_fact: mytests: "{{ mytests|d([]) + (lookup('file', item)|from_yaml).test }}" loop: "{{ query('fileglob', 'test_suites/QS_suite/*.yml') }}" - name: 输出结果 debug: var=mytests
运行后mytests始终仅包含最后一个文件的列表值,执行日志显示第一次循环赋值为file1的内容,第二次循环直接覆盖为file2的内容,没有累加。
做最小复现:仅收集匹配到的文件路径到列表,代码如下:
- name: 收集文件路径 set_fact: myfiles: "{{myfiles|d([]) + [item] }}" with_fileglob: - 'test_suites/QS_suite/*.yml' - name: 输出文件列表 debug: var=myfiles
执行后第二个文件路径同样无法追加到列表,仅保留最后一个匹配到的文件路径。
根本原因
Ansible默认在单任务的循环执行过程中,不会将同任务上一轮循环设置的fact值注入下一轮的变量计算上下文。每轮循环计算mytests|d([])时,读取到的都是任务启动前的初始变量状态(即未定义,被默认值过滤器替换为空列表),因此每轮赋值都是覆盖操作,最终仅保留最后一轮循环的结果。
解决方法
方法1:开启set_fact的可缓存参数(Ansible 2.8+适用)
给set_fact添加cacheable: true参数,让每轮循环设置的fact立即写入主机事实缓存,下一轮循环计算时即可读取到之前累加的结果:
- name: 拼接test列表 set_fact: mytests: "{{ mytests|d([]) + (lookup('file', item)|from_yaml).test }}" cacheable: true loop: "{{ query('fileglob', 'test_suites/QS_suite/*.yml') }}"
方法2:一次性批量合并(全版本兼容,推荐)
不通过循环逐次累加,直接用过滤器链批量完成文件读取、格式转换、字段提取、列表合并全流程,从根源上避免循环上下文的问题:
- name: 一次性合并所有文件的test列表 set_fact: mytests: "{{ query('fileglob', 'test_suites/QS_suite/*.yml') | map('lookup', 'file') | map('from_yaml') | map(attribute='test') | flatten }}"
代码逻辑说明:
- 先用
query('fileglob')获取所有匹配的yml文件路径列表 - 用
map('lookup', 'file')批量读取每个文件的文本内容 - 用
map('from_yaml')把所有文本内容解析为YAML结构化数据 - 用
map(attribute='test')提取每个YAML对象下的test列表 - 用
flatten把嵌套的多层列表拍平为单个一维列表
预期结果
两种方法修改后,执行debug输出的mytests结果为两个文件列表的完整拼接:
mytests: - start1 - begin1 - middle1 - end1 - start2 - begin2 - middle2 - end2
内容的提问来源于stack exchange,提问作者AEngineer
相关产品推荐
相关产品推荐

