抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

14. Ansible Playbook loop循环语句

目录

loop关键字说明

在ansible 2.5及以前的版本当中,所有的循环都是使用with_X风格。但是从2.6版本开始,官方开始推荐使用loop关键字来代替with_X风格的关键字。

在playbook中使用循环,直接使用loop关键字即可。

如下示例,启动httpd和postfix服务:

tasks:
- name: postfix and httpd are running
service:
name: "{{ item }}"
state: started
loop:
- postfix
- httpd

那么在这个示例当中,其实就是使用loop代替了with_list循环。

事实上,我们可以使用loop关键字搭配一些过滤器来替换更多的、更复杂的with_X循环。

loop_control

loop_control用于在循环时,获取列表的索引

- hosts: test
gather_facts: no
vars:
testlist:
- a
- [b,c,[e,f]]
- d
tasks:
- debug:
msg: "{{ index }}: {{ item }}"
loop: "{{ testlist | flatten(levels=1) }}"
loop_control:
index_var: index

输出结果:

PLAY [localhost] ******************************************************************
TASK [debug] **********************************************************************
ok: [localhost] => (item=a) => {
"msg": "0: a"
}
ok: [localhost] => (item=b) => {
"msg": "1: b"
}
ok: [localhost] => (item=c) => {
"msg": "2: c"
}
ok: [localhost] => (item=[u'e', u'f']) => {
"msg": "3: [u'e', u'f']"
}
ok: [localhost] => (item=d) => {
"msg": "4: d"
}
PLAY RECAP ************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

参数说明:

  • index_varloop_control的选项,让我们指定一个变量,loop_control会将列表元素的索引值存放到这个指定的变量中

with_list

loop可以替代with_list,当处理嵌套列表时,列表不会被拉平

- hosts: test
gather_facts: no
vars:
testlist:
- a
- [b,c]
- d
tasks:
- debug:
msg: "{{ item }}"
loop: "{{ testlist }}"

with_flattened

将所有嵌套都拉平

- hosts: test
gather_facts: no
vars:
testlist:
- a
- [b,c]
- d
tasks:
- debug:
msg: "{{ item }}"
loop: "{{ testlist| flatten }}"

with_items

只拉平第一层

- hosts: test
gather_facts: no
vars:
testlist:
- a
- [b,c]
- d
tasks:
- debug:
msg: "{{ item }}"
loop: "{{ testlist| flatten(levels=1) }}"

with_indexed_items

通过flatten过滤器(加参数),再配合loop_control关键字,可以替代with_indexed_items,当处理多层嵌套的列表时,只有列表中的第一层会被拉平

- hosts: test
gather_facts: no
vars:
testlist:
- a
- [b,c]
- d
tasks:
- debug:
msg: "{{ index }}: {{ item }}"
loop: "{{ testlist | flatten(levels=1) }}"
loop_control:
index_var: index

参数说明:

  • loop_control: 用于控制循环的行为,比如在循环时获取到元素的索引

  • index_varloop_control的选项,让我们指定一个变量,loop_control会将列表元素的索引值存放到这个指定的变量中

with_together

zip_longest过滤器配合list过滤器,可以替代with_together

- hosts: test
gather_facts: no
vars:
testlist1: [a,b]
testlist2: [1,2,3]
testlist3: [A,B,C,D]
tasks:
- debug:
msg: "{{ item.0 }} -- {{ item.1 }} -- {{ item.2 }}"
# [a,1,A],[b,2,B],['',3,C],['','',D]
with_together:
- "{{ testlist1 }}"
- "{{ testlist2 }}"
- "{{ testlist3 }}"
- debug:
# [a,1,A],[b,2,B]
msg: "{{ item.0 }} -- {{ item.1 }} -- {{ item.2 }}"
loop: "{{ testlist1 | zip_longest(testlist2,testlist3) | list}}"

当多个列表使用with_together进行对齐合并时,如果多个列表的长度不同,则使用最长的列表进行对齐,由于短列表中的元素数量不够,所以使用空值与长列表中的元素进行对齐,zip_longest过滤器也会像with_together一样,对列表进行组合,但是还需要借助list过滤器,将组合后的数据列表化。

在使用zip_longest过滤器代替with_together关键字时,默认也是使用空值与长列表中的元素进行对齐,但是也可以指定其他的字符串代替空值,如下示例即使用”NONE”代替空值:

- debug:
msg: "{{ item.0 }} - {{ item.1 }} - {{ item.2 }}"
loop: "{{ testlist1 | zip_longest(testlist2,testlist3,filevalue='NONE') | list }}"

zip_longest默认使用最长的列表长度进行对齐,当有多个列表的长度不同时,如果希望使用最短的列表对齐,则可以使用zip过滤器:

- debug:
msg: "{{ item.0 }} - {{ item.1 }} - {{ item.2 }}"
loop: "{{ testlist1 | zip(testlist2,testlist3) | list }}"

with_nested/with_cartesian

可使用product过滤器配合list过滤器以替代with_nested或者with_cartesian。product过滤器也是需要将组合后的数据进行列表化,所以需要与list过滤器配合使用:

- hosts: localhost
gather_facts: no
vars:
testlist1: [a,b,c]
testlist2: [1,2,3,4]
tasks:
- debug:
msg: "{{item.0}} --- {{ item.1}}"
loop: "{{ testlist1 | product(testlist2) | list}}"
# 结果
ok: [localhost] => (item=[u'a', 1]) => {
"msg": "a --- 1"
}
ok: [localhost] => (item=[u'a', 2]) => {
"msg": "a --- 2"
}
ok: [localhost] => (item=[u'a', 3]) => {
"msg": "a --- 3"
}
ok: [localhost] => (item=[u'a', 4]) => {
"msg": "a --- 4"
}
ok: [localhost] => (item=[u'b', 1]) => {
"msg": "b --- 1"
}
ok: [localhost] => (item=[u'b', 2]) => {
"msg": "b --- 2"
}
ok: [localhost] => (item=[u'b', 3]) => {
"msg": "b --- 3"
}
ok: [localhost] => (item=[u'b', 4]) => {
"msg": "b --- 4"
}
ok: [localhost] => (item=[u'c', 1]) => {
"msg": "c --- 1"
}
ok: [localhost] => (item=[u'c', 2]) => {
"msg": "c --- 2"
}
ok: [localhost] => (item=[u'c', 3]) => {
"msg": "c --- 3"
}
ok: [localhost] => (item=[u'c', 4]) => {
"msg": "c --- 4"
}

with_sequence

使用range过滤器配合list过滤器可以替代with_sequence:

- hosts: localhost
gather_facts: no
tasks:
- debug:
msg: "{{ item }}"
loop: "{{ range(0,6,2) | list }}"
# 结果
ok: [localhost] => (item=0) => {
"msg": 0
}
ok: [localhost] => (item=2) => {
"msg": 2
}
ok: [localhost] => (item=4) => {
"msg": 4

上例中表示生成数字,从0开始,到6结束,步长为2。但是需要说明的是,range函数的操作不包含结束范围,也就是说上面的循环只会生成0,2,4三个数字,而不包含6。

另外,with_sequence还有格式化的功能:

- debug:
msg: "{{ item }}"
with_sequence: start=2 end=6 stride=2 format="number is %0.2f"
# 结果
ok: [localhost] => (item=number is 2.00) => {
"msg": "number is 2.00"
}
ok: [localhost] => (item=number is 4.00) => {
"msg": "number is 4.00"
}
ok: [localhost] => (item=number is 6.00) => {
"msg": "number is 6.00"
}

可使用format配合loop实现:

- debug:
msg: "{{ 'number is %0.2f' |format(item) }}"
loop: "{{range(2,7,2) |list}}"
# 结果
ok: [localhost] => (item=number is 2.00) => {
"msg": "number is 2.00"
}
ok: [localhost] => (item=number is 4.00) => {
"msg": "number is 4.00"
}
ok: [localhost] => (item=number is 6.00) => {
"msg": "number is 6.00"
}

with_random_choice

使用random函数可以替代with_random_choice,由于random函数是随机取出列表中的一个值,并不涉及循环操作,所以并不使用loop关键字:

- hosts: test
gather_facts: no
vars:
testlist: [a,b,c]
tasks:
- debug:
msg: "{{ testlist | random }}"

with_dict

可使用loop配合dict2items过滤器实现with_dict功能:

- hosts: test
gather_facts: no
vars:
users:
alice: female
bob: male
tasks:
- debug:
msg: "{{ item.key }} is {{ item.value}}"
loop: "{{users | dict2items }}"

with_subelements

可使用loop配合subelements过滤器替代with_subelements:

- hosts: test
gather_facts: no
vars:
users:
- name: bob
gender: male
hobby:
- Skateboard
- VideoGame
- name: alice
gender: female
hobby:
- Music
tasks:
- debug:
msg: "{{ item.0.name }}'s hobby is {{ item.1}}"
with_subelements:
- "{{ users }}"
- hobby
- debug:
msg: "{{ item.0.name }}'s hobby is {{ item.1}}"
loop: "{{ users | subelements('hobby') }}"

使用zip_longest过滤器将两个列表中的元素对齐合并

- hosts: localhost
gather_facts: no
vars:
testlist1: [a,b]
testlist2: [1,2,3]
testlist3: [A,B,C,D]
tasks:
- debug:
msg: "['{{ item.0 }}','{{ item.1 }}' ,'{{ item.2 }}']" # a,1,A b,2,B '',3,c, '','',D
loop: "{{ testlist1 | zip_longest(testlist2,testlist3) | list}}"

输出结果如下:

TASK [debug] **********************************************************************
ok: [localhost] => (item=[u'a', 1, u'A']) => {
"msg": [
"a",
"1",
"A"
]
}
ok: [localhost] => (item=[u'b', 2, u'B']) => {
"msg": [
"b",
"2",
"B"
]
}
ok: [localhost] => (item=[None, 3, u'C']) => {
"msg": [
"",
"3",
"C"
]
}
ok: [localhost] => (item=[None, None, u'D']) => {
"msg": [
"",
"",
"D"
]
}

当多个列表进行对齐合并时,如果多个列表的长度不同,则使用最长的列表进行对齐,由于短列表中的元素数量不够,所以使用空值与长列表中的元素进行对齐,zip_longest过滤器会对列表进行组合,但是还需要借助list过滤器,将组合后的数据列表化。

在使用zip_longest过滤器时,默认使用空值与长列表中的元素进行对齐,但是也可以指定其他的字符串代替空值,如下示例即使用”NONE”代替空值:

- debug:
msg: "{{ item.0 }} - {{ item.1 }} - {{ item.2 }}"
loop: {{ testlist1 | zip_longest(testlist2,testlist3,filevalue='NONE') | list }}

zip_longest默认使用最长的列表长度进行对齐,当有多个列表的长度不同时,如果希望使用最短的列表对齐,则可以使用zip过滤器:

- debug:
msg: "{{ item.0 }} - {{ item.1 }} - {{ item.2 }}" # a,1,A b,2,B
loop: {{ testlist1 | zip(testlist2,testlist3) | list }}

在循环语句中注册变量

下面是一个register的变量在循环中使用的例子:

# cat register_loop.yml
- name: registered variable usage as a loop list
hosts: test
tasks:
- name: ensure /mnt/bkspool exists
file:
path: /mnt/bkspool
state: directory
- name: retrieve the list of home directories
command: ls /home
register: home_dirs
- name: Show home_dirs results
debug:
var: home_dirs.stdout_lines
- name: add home dirs to the backup spooler
file:
path: /mnt/bkspool/{{ item }}
src: /home/{{ item }}
state: link
force: yes
loop: "{{ home_dirs.stdout_lines }}"

在循环语句中注册变量:

- name: Loop Register test
gather_facts: no
hosts: test
tasks:
- name: Looping Echo Task
shell: "echo this is my item: {{ item }}"
loop:
- one
- two
register: echo_results
- name: Show echo_results variable
debug:
var: echo_results

执行语句,可以看到变量的返回结果为一个字典列表:

ok: [10.1.61.187] => {
"echo_results": {
"changed": true,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": true,
"cmd": "echo this is my item: one",
"delta": "0:00:00.004905",
"end": "2019-06-10 00:23:51.814151",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "echo this is my item: one",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": true
}
},
"item": "one",
"rc": 0,
"start": "2019-06-10 00:23:51.809246",
"stderr": "",
"stderr_lines": [],
"stdout": "this is my item: one",
"stdout_lines": [
"this is my item: one"
]
},
{
"ansible_loop_var": "item",
"changed": true,
"cmd": "echo this is my item: two",
"delta": "0:00:00.004736",
"end": "2019-06-10 00:23:52.008981",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "echo this is my item: two",
"_uses_shell": true,
"argv": null,
"chdir": null,
"creates": null,
"executable": null,
"removes": null,
"stdin": null,
"stdin_add_newline": true,
"strip_empty_ends": true,
"warn": true
}
},
"item": "two",
"rc": 0,
"start": "2019-06-10 00:23:52.004245",
"stderr": "",
"stderr_lines": [],
"stdout": "this is my item: two",
"stdout_lines": [
"this is my item: two"
]
}
]
}
}

评论