Ansible的循环:loop,with_<lookup>和until

环境

  • 管理节点:Ubuntu 22.04
  • 控制节点:CentOS 8
  • Ansible:2.15.6

循环的方法

  • loop
  • with_<lookup>
  • until

用这几种方式都可以实现循环。其中, loop 是推荐的用法,在很多时候能够替换 with_<lookup>

loopwith_<lookup>

with_<lookup> 使用了lookup插件,比如 with_items 使用的是 items lookup。(注:可参见我另一篇文档。)

loop 等同于 with_list 。注意, loop 是作用在list上的,如果用在字符串上会报错。

---
- hosts: alltasks:- name: task1debug:msg: "{{ item }}"loop: "{{ ['aaa', 'bbb', 'ccc'] }}"- name: task2debug:msg: "{{ item }}"with_list: "{{ ['aaa', 'bbb', 'ccc'] }}"- name: task3debug:msg: "{{ item }}"with_items: "{{ ['aaa', 'bbb', 'ccc'] }}"

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "aaa"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "bbb"
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "ccc"
}TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "aaa"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "bbb"
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "ccc"
}TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "aaa"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "bbb"
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "ccc"
}

可见,本例中 loopwith_listwith_items 的效果是一样的。

但是对于嵌套的list, loopwith_items 并不是完全等同的。

......- name: task4debug:msg: "{{ item }}"loop: "{{ ['aaa', ['bbb', ['ddd', 'eee']], 'ccc'] }}"- name: task5debug:msg: "{{ item }}"with_items: "{{ ['aaa', ['bbb', ['ddd', 'eee']], 'ccc'] }}"
......

运行结果如下:

TASK [task4] ***************************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "aaa"
}
ok: [192.168.1.55] => (item=['bbb', ['ddd', 'eee']]) => {"msg": ["bbb",["ddd","eee"]]
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "ccc"
}TASK [task5] ***************************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "aaa"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "bbb"
}
ok: [192.168.1.55] => (item=['ddd', 'eee']) => {"msg": ["ddd","eee"]
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "ccc"
}

可见:

  • loop 只针对最外层的list,不管是否有嵌套。
  • with_items 则是解开了第一层嵌套的list。这个行为比较诡异,要么就不要管嵌套,要么就全部处理,为什么只处理第一层嵌套呢?

实际上,对于 loop ,可用 flatten filter来指定解开嵌套:

(注:flatten是扁平化的意思,这里的扁平化和Java8里扁平化流的概念类似,即把层次结构转换为线性结构)

......- name: task6debug:msg: "{{ item }}"loop: "{{ ['aaa', ['bbb', ['ddd', 'eee']], 'ccc'] | flatten }}"- name: task7debug:msg: "{{ item }}"loop: "{{ ['aaa', ['bbb', ['ddd', 'eee']], 'ccc'] | flatten(levels=1) }}"
......

运行结果如下:

TASK [task6] ***************************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "aaa"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "bbb"
}
ok: [192.168.1.55] => (item=ddd) => {"msg": "ddd"
}
ok: [192.168.1.55] => (item=eee) => {"msg": "eee"
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "ccc"
}TASK [task7] ***************************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "aaa"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "bbb"
}
ok: [192.168.1.55] => (item=['ddd', 'eee']) => {"msg": ["ddd","eee"]
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "ccc"
}

可见, flatten 默认会处理所有嵌套,也可以通过 levels 选项,指定处理几层嵌套。

由于 with_items 处理一层嵌套,所以, with_items 就相当于 loop 指定了 flatten(levels=1) 。在本例中,task5和task7的运行结果是一样的。

需要使用 lookup 的循环,多使用 with_<lookup> 语句,而不是 loop 语句。比如:

---
- hosts: alltasks:- name: task1debug:msg: "{{ item }}"loop: "{{ lookup('fileglob', '/tmp/*.txt', wantlist=True) }}"- name: task2debug:msg: "{{ item }}"with_fileglob: "/tmp/*.txt"

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=/tmp/b.txt) => {"msg": "/tmp/b.txt"
}
ok: [192.168.1.55] => (item=/tmp/a.txt) => {"msg": "/tmp/a.txt"
}TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => (item=/tmp/b.txt) => {"msg": "/tmp/b.txt"
}
ok: [192.168.1.55] => (item=/tmp/a.txt) => {"msg": "/tmp/a.txt"
}

显然,此处使用 with_fileglob 比使用 loop 要简洁。

注: fileglob 获取指定目录下符合条件的文件名(不包含子目录)。

循环的种类

简单list

---
- hosts: allvars:var1: ['aaa', 'bbb', 'ccc']tasks:- name: task1 # list常量debug:msg: "{{ item }}"loop: "{{ ['aaa', 'bbb', 'ccc'] }}"# loop: ['aaa', 'bbb', 'ccc'] 可以简写- name: task2 # list常量debug:msg: "{{ item }}"loop:- aaa # 引号可以省略- "bbb"- "ccc"- name: task3 # list变量debug:msg: "{{ item }}"loop: "{{ var1 }}"

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "aaa"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "bbb"
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "ccc"
}TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "aaa"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "bbb"
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "ccc"
}TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "aaa"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "bbb"
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "ccc"
}

复杂list

---
- hosts: allvars:var1: [{name: "Tom", age: 20}, {name: "Jerry", age: 18}]tasks:- name: task1debug:msg: "Name: {{ item.name }}. Age: {{ item.age }}"loop: "{{ [{ 'name': 'Tom', 'age': 20 }, { 'name': 'Jerry', 'age': 18 }] }}"#loop: "{{ [{ name: 'Tom', age: 20 }, { name: 'Jerry', age: 18 }] }}" # 报错!说name未定义#loop: [{ name: 'Tom', age: 20 }, { name: 'Jerry', age: 18 }] # OK#loop: [{ 'name': 'Tom', 'age': 20 }, { 'name': 'Jerry', 'age': 18 }] # OK- name: task2debug:msg: "Name: {{ item.name }}. Age: {{ item.age }}"loop:- { name: "Tom", age: 20 }- { name: "Jerry", age: 18 }- name: task3debug:msg: "Name: {{ item.name }}. Age: {{ item.age }}"loop: "{{ var1 }}"

可以看到,对于key要不要加引号,行为好像有点诡异,最好还是加上吧。

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item={'name': 'Tom', 'age': 20}) => {"msg": "Name: Tom. Age: 20"
}
ok: [192.168.1.55] => (item={'name': 'Jerry', 'age': 18}) => {"msg": "Name: Jerry. Age: 18"
}TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => (item={'name': 'Tom', 'age': 20}) => {"msg": "Name: Tom. Age: 20"
}
ok: [192.168.1.55] => (item={'name': 'Jerry', 'age': 18}) => {"msg": "Name: Jerry. Age: 18"
}TASK [task3] ***************************************************************************************
ok: [192.168.1.55] => (item={'name': 'Tom', 'age': 20}) => {"msg": "Name: Tom. Age: 20"
}
ok: [192.168.1.55] => (item={'name': 'Jerry', 'age': 18}) => {"msg": "Name: Jerry. Age: 18"
}

dict

如果要遍历一个dict,则需要使用 dict2items filter,将其转换为list:

---
- hosts: allvars:var1: {name: "Tom", age: 20}tasks:- name: task1debug:msg: "Key: {{ item.key }}. Value: {{ item.value }}"loop: "{{ {'name': 'Tom', 'age': 20} | dict2items }}"- name: task2debug:msg: "Key: {{ item.key }}. Value: {{ item.value }}"loop: "{{ var1 | dict2items }}"

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item={'key': 'name', 'value': 'Tom'}) => {"msg": "Key: name. Value: Tom"
}
ok: [192.168.1.55] => (item={'key': 'age', 'value': 20}) => {"msg": "Key: age. Value: 20"
}TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => (item={'key': 'name', 'value': 'Tom'}) => {"msg": "Key: name. Value: Tom"
}
ok: [192.168.1.55] => (item={'key': 'age', 'value': 20}) => {"msg": "Key: age. Value: 20"
}

本例中,dict为:

{name: "Tom", age: 20}

转为list后:

    [{"key": "name","value": "Tom"},{"key": "age","value": 20}]

循环结果的register变量

---
- hosts: alltasks:- name: task1shell: "echo {{ item }}"loop: "{{ ['aaa', 'bbb'] }}"register: var1- name: task2debug:msg: "{{ var1 }}"

运行结果如下:

TASK [task1] ***************************************************************************************
changed: [192.168.1.55] => (item=aaa)
changed: [192.168.1.55] => (item=bbb)TASK [task2] ***************************************************************************************
ok: [192.168.1.55] => {"msg": {"changed": true,"msg": "All items completed","results": [{"ansible_loop_var": "item","changed": true,"cmd": "echo aaa","delta": "0:00:00.002332","end": "2023-11-21 22:29:54.990234","failed": false,"invocation": {"module_args": {"_raw_params": "echo aaa","_uses_shell": true,"argv": null,"chdir": null,"creates": null,"executable": null,"removes": null,"stdin": null,"stdin_add_newline": true,"strip_empty_ends": true}},"item": "aaa","msg": "","rc": 0,"start": "2023-11-21 22:29:54.987902","stderr": "","stderr_lines": [],"stdout": "aaa","stdout_lines": ["aaa"]},{"ansible_loop_var": "item","changed": true,"cmd": "echo bbb","delta": "0:00:00.002036","end": "2023-11-21 22:29:55.223227","failed": false,"invocation": {"module_args": {"_raw_params": "echo bbb","_uses_shell": true,"argv": null,"chdir": null,"creates": null,"executable": null,"removes": null,"stdin": null,"stdin_add_newline": true,"strip_empty_ends": true}},"item": "bbb","msg": "","rc": 0,"start": "2023-11-21 22:29:55.221191","stderr": "","stderr_lines": [],"stdout": "bbb","stdout_lines": ["bbb"]}],"skipped": false}
}

可见,register变量把循环操作的结果放到了叫做 results 的list里。因此,后续可以遍历 results ,做相应处理,比如:

---
- hosts: alltasks:- name: task1shell: "cat {{ item }}"loop: "{{ ['/tmp/a.txt', '/tmp/d.txt'] }}"register: var1ignore_errors: true- name: task2debug:msg: "{{ var1 }}"- name: task3fail:msg: "Something is wrong!"when: item.rc != 0loop: "{{ var1.results }}"

假设 /tmp/d.txt 不存在,则运行结果如下:

TASK [task1] ***************************************************************************************
changed: [192.168.1.55] => (item=/tmp/a.txt)
failed: [192.168.1.55] (item=/tmp/d.txt) => {"ansible_loop_var": "item", "changed": true, "cmd": "cat /tmp/d.txt", "delta": "0:00:00.002438", "end": "2023-11-21 22:46:28.216904", "item": "/tmp/d.txt", "msg": "non-zero return code", "rc": 1, "start": "2023-11-21 22:46:28.214466", "stderr": "cat: /tmp/d.txt: No such file or directory", "stderr_lines": ["cat: /tmp/d.txt: No such file or directory"], "stdout": "", "stdout_lines": []}
...ignoringTASK [task2] ***************************************************************************************
ok: [192.168.1.55] => {"msg": {"changed": true,"failed": true,"msg": "One or more items failed","results": [{"ansible_loop_var": "item","changed": true,"cmd": "cat /tmp/a.txt","delta": "0:00:00.003006","end": "2023-11-21 22:46:27.995302","failed": false,"invocation": {"module_args": {"_raw_params": "cat /tmp/a.txt","_uses_shell": true,"argv": null,"chdir": null,"creates": null,"executable": null,"removes": null,"stdin": null,"stdin_add_newline": true,"strip_empty_ends": true}},"item": "/tmp/a.txt","msg": "","rc": 0,"start": "2023-11-21 22:46:27.992296","stderr": "","stderr_lines": [],"stdout": "aaaaa\nb\nccccc","stdout_lines": ["aaaaa","b","ccccc"]},{"ansible_loop_var": "item","changed": true,"cmd": "cat /tmp/d.txt","delta": "0:00:00.002438","end": "2023-11-21 22:46:28.216904","failed": true,"invocation": {"module_args": {"_raw_params": "cat /tmp/d.txt","_uses_shell": true,"argv": null,"chdir": null,"creates": null,"executable": null,"removes": null,"stdin": null,"stdin_add_newline": true,"strip_empty_ends": true}},"item": "/tmp/d.txt","msg": "non-zero return code","rc": 1,"start": "2023-11-21 22:46:28.214466","stderr": "cat: /tmp/d.txt: No such file or directory","stderr_lines": ["cat: /tmp/d.txt: No such file or directory"],"stdout": "","stdout_lines": []}],"skipped": false}
}TASK [task3] ***************************************************************************************
skipping: [192.168.1.55] => (item={'changed': True, 'stdout': 'aaaaa\nb\nccccc', 'stderr': '', 'rc': 0, 'cmd': 'cat /tmp/a.txt', 'start': '2023-11-21 22:46:27.992296', 'end': '2023-11-21 22:46:27.995302', 'delta': '0:00:00.003006', 'msg': '', 'invocation': {'module_args': {'_raw_params': 'cat /tmp/a.txt', '_uses_shell': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['aaaaa', 'b', 'ccccc'], 'stderr_lines': [], 'failed': False, 'item': '/tmp/a.txt', 'ansible_loop_var': 'item'}) 
failed: [192.168.1.55] (item={'changed': True, 'stdout': '', 'stderr': 'cat: /tmp/d.txt: No such file or directory', 'rc': 1, 'cmd': 'cat /tmp/d.txt', 'start': '2023-11-21 22:46:28.214466', 'end': '2023-11-21 22:46:28.216904', 'delta': '0:00:00.002438', 'failed': True, 'msg': 'non-zero return code', 'invocation': {'module_args': {'_raw_params': 'cat /tmp/d.txt', '_uses_shell': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': [], 'stderr_lines': ['cat: /tmp/d.txt: No such file or directory'], 'item': '/tmp/d.txt', 'ansible_loop_var': 'item'}) => {"ansible_loop_var": "item", "changed": false, "item": {"ansible_loop_var": "item", "changed": true, "cmd": "cat /tmp/d.txt", "delta": "0:00:00.002438", "end": "2023-11-21 22:46:28.216904", "failed": true, "invocation": {"module_args": {"_raw_params": "cat /tmp/d.txt", "_uses_shell": true, "argv": null, "chdir": null, "creates": null, "executable": null, "removes": null, "stdin": null, "stdin_add_newline": true, "strip_empty_ends": true}}, "item": "/tmp/d.txt", "msg": "non-zero return code", "rc": 1, "start": "2023-11-21 22:46:28.214466", "stderr": "cat: /tmp/d.txt: No such file or directory", "stderr_lines": ["cat: /tmp/d.txt: No such file or directory"], "stdout": "", "stdout_lines": []}, "msg": "Something is wrong!"}

本例中,由于 /tmp/d.txt 不存在, results 的第2个元素,其rc值为1。

注意:每一次迭代,其结果就会放到register变量里,而不是整个循环结束后才放的。

---
- hosts: alltasks:- name: task1shell: echo "{{ item }}"loop:- "aaa"- "bbb"register: var1changed_when: var1.stdout != "aaa"

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=aaa)
changed: [192.168.1.55] => (item=bbb)

可见,第一次迭代没有满足判断条件,而第二次迭代满足判断条件了。

复杂循环

遍历嵌套list

---
- hosts: alltasks:- name: task1debug:msg: "{{ item[0] }} {{ item[1] }}"loop: "{{ ['Zhang', 'Li'] | product(['San', 'Si']) | list }}"

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=['Zhang', 'San']) => {"msg": "Zhang San"
}
ok: [192.168.1.55] => (item=['Zhang', 'Si']) => {"msg": "Zhang Si"
}
ok: [192.168.1.55] => (item=['Li', 'San']) => {"msg": "Li San"
}
ok: [192.168.1.55] => (item=['Li', 'Si']) => {"msg": "Li Si"
}

本例中,把两个list做笛卡尔乘积,生成了一个新的嵌套list:

    [["Zhang","San"],["Zhang","Si"],["Li","San"],["Li","Si"]]

然后遍历外层list,并通过 item[0]item[1] 访问内层list的元素。

Retry

---
- hosts: alltasks:- name: task1shell: cat /tmp/a.txtregister: var1until: var1.stdout.find("OK") != -1retries: 3delay: 5

运行结果如下:

TASK [task1] ***************************************************************************************
FAILED - RETRYING: [192.168.1.55]: task1 (3 retries left).
FAILED - RETRYING: [192.168.1.55]: task1 (2 retries left).
FAILED - RETRYING: [192.168.1.55]: task1 (1 retries left).
fatal: [192.168.1.55]: FAILED! => {"attempts": 3, "changed": true, "cmd": "cat /tmp/a.txt", "delta": "0:00:00.002228", "end": "2023-11-23 07:53:18.333193", "msg": "", "rc": 0, "start": "2023-11-23 07:53:18.330965", "stderr": "", "stderr_lines": [], "stdout": "aaaaa\nb\nccccc", "stdout_lines": ["aaaaa", "b", "ccccc"]}

在运行过程中,编辑 /tmp/a.txt 文件(注意是在目标机器上),添加 OK 的内容,则运行结果如下:

TASK [task1] ***************************************************************************************
FAILED - RETRYING: [192.168.1.55]: task1 (3 retries left).
FAILED - RETRYING: [192.168.1.55]: task1 (2 retries left).
changed: [192.168.1.55]

注:

  • retries 的缺省值是 3delay 的缺省值是 5

遍历inventory

假设 /etc/ansible/hosts 内容如下:

[myvms]
192.168.1.55[myself]
127.0.0.1
---
- hosts: alltasks:- name: task1debug:msg: "{{ item }}"loop: "{{ groups['all'] }}"#loop: "{{ groups['myvms'] }}"

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=192.168.1.55) => {"msg": "192.168.1.55"
}
ok: [192.168.1.55] => (item=127.0.0.1) => {"msg": "127.0.0.1"
}
ok: [127.0.0.1] => (item=192.168.1.55) => {"msg": "192.168.1.55"
}
ok: [127.0.0.1] => (item=127.0.0.1) => {"msg": "127.0.0.1"
}

可见,打印了所有的主机名。

为什么打印了两次呢?这是因为指定了 hosts: all ,所以在两个目标机器上都运行了一次。

若改为 hosts: myvms ,则运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=192.168.1.55) => {"msg": "192.168.1.55"
}
ok: [192.168.1.55] => (item=127.0.0.1) => {"msg": "127.0.0.1"
}

如果只想遍历 myvms ,则把 loop: "{{ groups['all'] }}" 改为 loop: "{{ groups['myvms'] }}" ,运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=192.168.1.55) => {"msg": "192.168.1.55"
}

也可以通过 loop: "{{ ansible_play_batch }}" 指定遍历当前play的主机:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=192.168.1.55) => {"msg": "192.168.1.55"
}

注: groupsansible_play_batch 都是Ansible的特殊变量,参见 https://docs.ansible.com/ansible/latest/reference_appendices/special_variables.html

还可以通过 inventory_hostnames lookup来指定遍历的主机:

---
- hosts: myvmstasks:- name: task1debug:msg: "{{ item }}"loop: "{{ query('inventory_hostnames', 'all') }}"

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=192.168.1.55) => {"msg": "192.168.1.55"
}
ok: [192.168.1.55] => (item=127.0.0.1) => {"msg": "127.0.0.1"
}

遍历 all ,同时排除 myvms ,则指定:loop: "{{ query('inventory_hostnames', 'all:!myvms') }}"

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=127.0.0.1) => {"msg": "127.0.0.1"
}

loop_control

label

前面我们提到,遍历一个dict:

---
- hosts: allvars:var1: {name: "Tom", age: 20}tasks:- name: task1debug:msg: "Key: {{ item.key }}. Value: {{ item.value }}"loop: "{{ {'name': 'Tom', 'age': 20} | dict2items }}"#loop_control:#  label: "{{ item.key}}"

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item={'key': 'name', 'value': 'Tom'}) => {"msg": "Key: name. Value: Tom"
}
ok: [192.168.1.55] => (item={'key': 'age', 'value': 20}) => {"msg": "Key: age. Value: 20"
}

注意其中的 (item={'key': 'name', 'value': 'Tom'}) 等,如果item数据量很大,则输出量很大。此处可以使用 label 指定打印的内容(比如只打印key,不打印value),见注释部分。

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=name) => {"msg": "Key: name. Value: Tom"
}
ok: [192.168.1.55] => (item=age) => {"msg": "Key: age. Value: 20"
}

pause

在每次循环迭代之间,暂停一段时间(秒)。

---
- hosts: alltasks:- name: task1debug:msg: "{{ item }}"loop: "{{ ['aaa', 'bbb', 'ccc'] }}"loop_control:pause: 3

index_var

指定下标变量,然后通过该变量获取下标值(从0开始)。

---
- hosts: alltasks:- name: task1debug:msg: "{{ idx }}: {{ item }}"loop: "{{ ['aaa', 'bbb', 'ccc'] }}"loop_control:index_var: idx

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "0: aaa"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "1: bbb"
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "2: ccc"
}

loop_var

循环的元素名称,默认叫做 item ,而对于嵌套循环,为避免混淆内外循环的item,可用 loop_var 指定item名称。

创建文件 test19.yml 如下:

---
- hosts: alltasks:- name: task1include_tasks: test20.ymlloop: [1, 2, 3]loop_control:loop_var: item_outer

创建文件 test20.yml 如下:

---
- name: inner_task1debug:msg: "Outer item = {{ item_outer }}, Inner item = {{ item_inner }}"loop: "{{ ['aaa', 'bbb', 'ccc'] }}"loop_control:loop_var: item_inner

运行结果如下:

TASK [task1] ***************************************************************************************
included: /root/temp/temp1121/test20.yml for 192.168.1.55 => (item=1)
included: /root/temp/temp1121/test20.yml for 192.168.1.55 => (item=2)
included: /root/temp/temp1121/test20.yml for 192.168.1.55 => (item=3)TASK [inner_task1] *********************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "Outer item = 1, Inner item = aaa"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "Outer item = 1, Inner item = bbb"
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "Outer item = 1, Inner item = ccc"
}TASK [inner_task1] *********************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "Outer item = 2, Inner item = aaa"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "Outer item = 2, Inner item = bbb"
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "Outer item = 2, Inner item = ccc"
}TASK [inner_task1] *********************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "Outer item = 3, Inner item = aaa"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "Outer item = 3, Inner item = bbb"
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "Outer item = 3, Inner item = ccc"
}

本例中,外部循环的item命名为 item_outer ,而内部循环的item命名为 item_inner

扩展循环变量

添加 extended: true ,则可以访问如下变量:

  • ansible_loop.allitems :所有元素
  • ansible_loop.index :从1开始
  • ansible_loop.index0 :从0开始
  • ansible_loop.revindex :倒数,从1开始
  • ansible_loop.revindex0 :倒数,从0开始
  • ansible_loop.first :是否是第一个元素
  • ansible_loop.last :是否是最后一个元素
  • ansible_loop.length :元素数量
  • ansible_loop.previtem :前一个元素(第一次迭代时未定义)
  • ansible_loop.nextitem :后一个元素(最后一次迭代时未定义)
---
- hosts: alltasks:- name: task1debug:msg: "ansible_loop.allitems = {{ ansible_loop.allitems }}, ansible_loop.index = {{ ansible_loop.index }}, ansible_loop.index0 = {{ ansible_loop.index0 }}, ansible_loop.revindex = {{ ansible_loop.revindex }}, ansible_loop.revindex0 = {{ ansible_loop.revindex0 }}, ansible_loop.first = {{ ansible_loop.first }}, ansible_loop.last = {{ ansible_loop.last }}, ansible_loop.length = {{ ansible_loop.length }}, ansible_loop.previtem = {{ ansible_loop.previtem | default('no previous') }}, ansible_loop.nextitem = {{ ansible_loop.nextitem | default('no next') }}"loop: "{{ ['aaa', 'bbb', 'ccc'] }}"loop_control:extended: true

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=aaa) => {"msg": "ansible_loop.allitems = ['aaa', 'bbb', 'ccc'], ansible_loop.index = 1, ansible_loop.index0 = 0, ansible_loop.revindex = 3, ansible_loop.revindex0 = 2, ansible_loop.first = True, ansible_loop.last = False, ansible_loop.length = 3, ansible_loop.previtem = no previous, ansible_loop.nextitem = bbb"
}
ok: [192.168.1.55] => (item=bbb) => {"msg": "ansible_loop.allitems = ['aaa', 'bbb', 'ccc'], ansible_loop.index = 2, ansible_loop.index0 = 1, ansible_loop.revindex = 2, ansible_loop.revindex0 = 1, ansible_loop.first = False, ansible_loop.last = False, ansible_loop.length = 3, ansible_loop.previtem = aaa, ansible_loop.nextitem = ccc"
}
ok: [192.168.1.55] => (item=ccc) => {"msg": "ansible_loop.allitems = ['aaa', 'bbb', 'ccc'], ansible_loop.index = 3, ansible_loop.index0 = 2, ansible_loop.revindex = 1, ansible_loop.revindex0 = 0, ansible_loop.first = False, ansible_loop.last = True, ansible_loop.length = 3, ansible_loop.previtem = bbb, ansible_loop.nextitem = no next"
}

注:如果 ansible_loop.allitems 很大,为了节省内存,可以设置 extended_allitems: false

loop_control:extended: trueextended_allitems: false

获取 loop_var 的值

比如指定了 loop_var: myitem ,则可以通过 {{ myitem }} 来获取item,也可以通过 {{ lookup('vars', ansible_loop_var) }} 获取item。

---
- hosts: alltasks:- name: task1debug:msg: "{{ myitem }} , {{ lookup('vars', ansible_loop_var) }}"loop: [1, 2, 3]loop_control:loop_var: myitem

运行结果如下:

TASK [task1] ***************************************************************************************
ok: [192.168.1.55] => (item=1) => {"msg": "1 , 1"
}
ok: [192.168.1.55] => (item=2) => {"msg": "2 , 2"
}
ok: [192.168.1.55] => (item=3) => {"msg": "3 , 3"
}

可见,二者效果是一样的。

参考

  • https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html
  • https://docs.ansible.com/ansible/latest/reference_appendices/special_variables.html
  • https://docs.ansible.com/ansible/latest/inventory_guide/intro_patterns.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/169178.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Loadrunner安装大全

目录 一 、下载篇 二、安装篇 三、破解篇 四、Loadrunner支持哪些操作系统&#xff1f; 五、安装Loadrunner需要满足哪些系统要求&#xff1f; 六、安装Loadrunner时是否需要注意什么问题&#xff1f; 七、安装完成后如何验证Loadrunner是否正常工作&#xff1f; 八、如…

【每日一题】二叉树中的伪回文路径

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;递归&#xff08;DFS&#xff09;方法二&#xff1a;位运算 写在最后 Tag 【递归/DFS】【伪回文】【二叉树】【2023-11-25】 题目来源 1457. 二叉树中的伪回文路径 题目解读 伪回文路径指的是路径中的节点值经过重新…

基于51单片机的FM数字收音机系统电路设计

**单片机设计介绍&#xff0c;基于51单片机的FM数字收音机系统电路设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于51单片机的FM数字收音机系统是一种用于接收和播放FM广播信号的设备&#xff0c;以下是一个基本的电路设…

ubuntu22.04 安装 jupyterlab

JupyterLab Install JupyterLab with pip: pip install jupyterlabNote: If you install JupyterLab with conda or mamba, we recommend using the conda-forge channel. Once installed, launch JupyterLab with: jupyter lab

Pycharm创建项目新环境,安装Pytorch

在python项目中&#xff0c;很多项目使用的各类包的版本是不一致的。所以我们可以对每个项目有专属于它的环境。所以这个文章就是教你如何创建新环境。 一、创建新环境 首先我们需要去官网下载conda。然后在Pycharm下面添加conda的可执行文件。 用conda创建新环境。 二、…

CSS新特性(2-2)

CSS新特性&#xff08;2-2&#xff09; 前言box相关box-shadow background背景rgba颜色与透明度transform:rotate(Xdeg) 2D旋转transform:tranlate 平移 前言 本文继续讲解CSS3其他的新特性&#xff0c;想看之前新特性点击这里&#xff0c;那么好本文正式开始。 box相关 box…

二十三种设计模式全面解析-揭秘访问者模式:开启对象间灵活交互之门

在软件设计中&#xff0c;我们经常会遇到需要对一组对象进行操作和处理的情况。但是&#xff0c;如果对象结构复杂且对象类型众多&#xff0c;传统的处理方式可能会导致代码的冗长和维护困难。然而&#xff0c;访问者模式&#xff08;Visitor Pattern&#xff09;以其优雅的解决…

开源与闭源

我的观点&#xff1a; 开源与闭源软件都有各自的优势和劣势&#xff0c;没有绝对的对错之分。.. 一、开源和闭源的优劣势比较 开源的好处与劣处 优势&#xff1a; 创新与合作&#xff1a;开源软件能够吸引更多的开发者参与到项目中来&#xff0c;促进创新和合作。开放的源代码…

Redis(一):初始Redis

Redis基本介绍 Redis 的全称是 Remote Dictionary Server&#xff0c;Redis是一个开放源代码、内存数据结构存储系统&#xff0c;被广泛应用于缓存、消息队列、实时统计等场景。Redis使用C编写&#xff0c;支持多种数据结构&#xff0c;例如字符串&#xff08;strings&#xf…

【ArcGIS Pro微课1000例】0036:栅格影像裁剪与提取(矢量范围裁剪dem高程数据)

本实验讲解在ArcGIS Pro中进行栅格影像裁剪与提取(矢量范围裁剪dem高程数据)的方法。DEM、DOM、DSM等栅格数据方法也可以实现。 文章目录 一、加载实验数据二、裁剪工具的使用1. 裁剪栅格2. 按掩膜提取一、加载实验数据 加载配套实验数据包中的0036.rar中的dem数据和矢量裁剪…

jconsole的基本使用和死锁的检测

jconsole的基本使用和死锁的检测 因为jconsole是JDK自带的&#xff0c;所以安装了JDK就可以直接打开了。 1. 打开方式 cmd命令行打开&#xff1a;输入jconsole&#xff0c;然后按Enter JDK安装目录&#xff0c;bin目录下&#xff0c;双击即可打开 选择一个进程然后打开 可…

C语言字符串

字符串 字符串字面量 字符串字面量是用一对双引号括起来的字符序列。 例&#xff1a;"When you come to a fork in the road,take it." 延续字符串字面量 如果发现字符串字面量太长而无法放置在单独一行以内&#xff0c;只要把第一行用字符 \ 结尾&#xff0c;那…

深入解析Selenium动作链:精通点击、拖拽、切换等操作

背景&#xff1a; 一些交互动作都是针对某个节点执行的。比如&#xff0c;对于输入框&#xff0c;我们就调用它的输入文字和清空文字方法&#xff1b;对于按钮&#xff0c;就调用它的点击方法。其实&#xff0c;还有另外一些操作&#xff0c;它们没有特定的执行对象&#xff0…

中国信通院王蕴韬:从“好用”到“高效”,AIGC需要被再次颠覆

当下AIGC又有了怎样的颠覆式技术&#xff1f;处于一个怎样的发展阶段&#xff1f;产业应用如何&#xff1f;以及存在哪些风险&#xff1f;针对这些问题&#xff0c;我们与中国信通院云计算与大数据研究所副总工程师王蕴韬进行了一次深度对话&#xff0c;从他哪里找到了这些问题…

C#中的警告CS0120、CS0176、CS0183、CS0618、CS8600、CS8602、CS8604、CS8625及处理

目录 一、CS0120 二、CS0176 1.解决前 2.解决后 3.解决办法 三、CS0183 四、CS0618 五、CS8600 六、CS8602 七、CS8622 1. 解决前&#xff1a; 2. 解决后&#xff1a; 3.解决方法&#xff1a; 八、CS8604和CS8625 一、CS0120 严重性 代码 说明 项目 文件 行…

电路 buck-boost相关知识

BUCK-BOOST 文章目录 BUCK-BOOST前言一、DC-DC工作模式电容电感特性伏秒积平衡原理 二、BUCK电路三、BOOST电路四、BUCK-BOOST电路总结 前言 最近需要用到buck-boost相关的电路知识&#xff0c;于是便写下这篇文章复习一下。 一、DC-DC 在学习buck-boost电路之前我们先来看一…

邮件泄密案例分析

近日&#xff0c;一起令人震惊的事件在美军方内部引发了广泛关注。据报道&#xff0c;美军方意外将数百万封包含敏感信息的邮件发至非洲国家马里。这些邮件涉及的内容十分广泛&#xff0c;包括军事行动计划、人员部署、战术策略等&#xff0c;甚至还有部分涉及国家安全和战略决…

前缀和——238. 除自身以外数组的乘积

文章目录 &#x1f377;1. 题目&#x1f378;2. 算法原理&#x1f365;解法一&#xff1a;暴力求解&#x1f365;解法二&#xff1a;前缀和&#xff08;积&#xff09; &#x1f379;3. 代码实现 &#x1f377;1. 题目 题目链接&#xff1a;238. 除自身以外数组的乘积 - 力扣&a…

代码随想录算法训练营第五十八天|739. 每日温度、496. 下一个更大元素 I

第十章 单调栈part01 739. 每日温度 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0c;请在该位置用…

小程序项目:springboot+vue基本微信小程序的学生健康管理系统

项目介绍 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时…