解决Ansible遍历清单组主机执行文件更新任务的循环问题
问题:Ansible多主机组遍历执行文件更新任务失败
现有配置
Playbook代码
--- - hosts: dev gather_facts: yes become: true become_method: su become_user: xxxxx tasks: - name: Update the file contents replace: path: "/path/to/file{{ item.path }}.xml" regexp: '(xxxxxxx[\s\S]*yyyyyyy$)' replace: "/zzz/zzzz/{{ item.replace }}.dat /qqqq/qqqqq/q" backup: yes with_items: - { path: "{{ groups['dev'] }}", replace: "{{ groups['pc'] }}" }
Inventory配置
[dev] host1 host2 [pc] 1234 6789
执行报错
failed: [host1] (item={u'path': [u'host1', u'host2'], u'replace': [u'1234', u'6789']}) => {"ansible_loop_var": "item", "changed": false, " item": {"path": ["host1", "host2"], "replace": ["1234", "6789"]}, "msg": "Path /path/to/file[u'host1', u'host2'].xml does not exist !", "rc": 257} failed: [host2] (item={u'path': [u'host1', u'host2'], u'replace': [u'1234', u'6789']}) => {"ansible_loop_var": "item", "changed": false, " item": {"path": ["host1", "host2"], "replace": ["1234", "6789"]}, "msg": "Path /path/to/file[u'host1', u'host2'].xml does not exist !", rc": 257}
期望效果
[host1] (item={u'path': [u'host1'], u'replace': [u'1234']}) "msg": "Path /path/to/file/host1.xml [host2] (item={u'path': [u'host2'], u'replace': [u'6789']}) "msg": "Path /path/to/file/host2.xml
问题原因
groups['dev']和groups['pc']会返回整个组的列表,而非当前主机对应的单个值。当dev组存在多台主机时,item.path被赋值为整个dev主机列表,导致文件路径拼接错误,无法找到目标文件。
解决方案
方案1:利用主机索引匹配对应值
通过当前主机在dev组中的索引,获取pc组对应位置的值,实现一一匹配:
--- - hosts: dev gather_facts: yes become: true become_method: su become_user: xxxxx tasks: - name: Update the file contents replace: path: "/path/to/file{{ inventory_hostname }}.xml" regexp: '(xxxxxxx[\s\S]*yyyyyyy$)' replace: "/zzz/zzzz/{{ groups['pc'][groups['dev'].index(inventory_hostname)] }}.dat /qqqq/qqqqq/q" backup: yes
- 说明:
inventory_hostname直接调用当前执行任务的主机名,无需遍历组列表groups['dev'].index(inventory_hostname)获取当前主机在dev组的位置索引- 用该索引从pc组中取出对应值,完成dev主机与pc值的一一对应
方案2:预定义主机-替换值映射列表
直接在Playbook中明确每个主机对应的替换值,通过条件判断执行任务:
--- - hosts: dev gather_facts: yes become: true become_method: su become_user: xxxxx vars: host_replace_map: - { host: "host1", replace_val: "1234" } - { host: "host2", replace_val: "6789" } tasks: - name: Update the file contents replace: path: "/path/to/file{{ item.host }}.xml" regexp: '(xxxxxxx[\s\S]*yyyyyyy$)' replace: "/zzz/zzzz/{{ item.replace_val }}.dat /qqqq/qqqqq/q" backup: yes when: inventory_hostname == item.host loop: "{{ host_replace_map }}"
- 说明:
- 定义
host_replace_map清晰展示主机与替换值的对应关系 - 通过
when条件仅在当前主机匹配时执行任务,避免无效遍历
- 定义
方案3:使用Inventory主机变量(推荐)
修改Inventory,直接给dev组主机添加专属变量,实现最简洁的配置:
修改后的Inventory:
[dev] host1 replace_val=1234 host2 replace_val=6789 [pc] 1234 6789
简化后的Playbook:
--- - hosts: dev gather_facts: yes become: true become_method: su become_user: xxxxx tasks: - name: Update the file contents replace: path: "/path/to/file{{ inventory_hostname }}.xml" regexp: '(xxxxxxx[\s\S]*yyyyyyy$)' replace: "/zzz/zzzz/{{ replace_val }}.dat /qqqq/qqqqq/q" backup: yes
- 说明:
- 每个dev主机直接携带自身的
replace_val变量,无需处理组索引或额外列表 - 代码最简洁,维护成本最低,是最优解决方案
- 每个dev主机直接携带自身的
内容的提问来源于stack exchange,提问作者Tan
相关产品推荐
相关产品推荐

