小技巧 - 如何在迴圈裡註冊變數
單一個模組執行結果,可以使用 register 把結果放到變數裡。
如果這模組要搭配迴圈執行多次,又要把結果放到變數裡時,要怎麼寫呢?因為之前沒有用過,腦袋不禁打結了,心裡開始在盤算該怎麼處理比較好?但好在,早有人提出解答。
StackOverflow 上的說明:Register variables in with_items loop in Ansible playbook
簡單的說,不需要特別做什麼處理,照寫就可以,只是取得的變數格式有些不同。取得的變數會把每次執行的結果放到 .results 裡,存為一個 list。舉個例子來說明,在沒有使用 loop 的情況是這樣:
---
- name: Register without loop
hosts: all
tasks:
- name: shell
shell: hostname
register: shell_result
- name: display
debug:
var: shell_result
輸出結果如下(摘錄)
TASK [display] ************************************************************************************************
ok: [localhost] => {
"shell_result": {
"changed": true,
"cmd": "hostname",
"delta": "0:00:00.004216",
"end": "2022-09-03 05:59:29.924106",
"failed": false,
"rc": 0,
"start": "2022-09-03 05:59:29.919890",
"stderr": "",
"stderr_lines": [],
"stdout": "example-host",
"stdout_lines": [
"example-host"
]
}
}
PLAY RECAP ****************************************************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
這時可以看到 shell_result 是個 dict 型態的變數。
再加上 loop 以後,同樣,舉個例子來看。
---
- name: Register with loop
hosts: all
vars:
cmd_list:
- hostname
- date
- uptime
tasks:
- name: shell
shell: "{{ item }}"
register: shell_result
loop: "{{ cmd_list }}"
- name: display
debug:
var: shell_result
輸出結果如下(摘錄),這時就會看到 shell_result 多了一個 list 型態的 results 屬性。
TASK [display] ************************************************************************************************
ok: [localhost] => {
"shell_result": {
"changed": true,
"msg": "All items completed",
"results": [
{
"ansible_loop_var": "item",
"changed": true,
"cmd": "hostname",
"delta": "0:00:00.003129",
"end": "2022-09-03 06:12:38.139077",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "hostname",
"_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": "hostname",
"rc": 0,
"start": "2022-09-03 06:12:38.135948",
"stderr": "",
"stderr_lines": [],
"stdout": "example-host",
"stdout_lines": [
"example-host"
]
},
{
"ansible_loop_var": "item",
"changed": true,
"cmd": "date",
"delta": "0:00:00.009256",
"end": "2022-09-03 06:12:38.286830",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "date",
"_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": "date",
"rc": 0,
"start": "2022-09-03 06:12:38.277574",
"stderr": "",
"stderr_lines": [],
"stdout": "西元2022年09月03日 (週六) 06時12分38秒 CST",
"stdout_lines": [
"西元2022年09月03日 (週六) 06時12分38秒 CST"
]
},
{
"ansible_loop_var": "item",
"changed": true,
"cmd": "uptime",
"delta": "0:00:00.004768",
"end": "2022-09-03 06:12:38.433260",
"failed": false,
"invocation": {
"module_args": {
"_raw_params": "uptime",
"_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": "uptime",
"rc": 0,
"start": "2022-09-03 06:12:38.428492",
"stderr": "",
"stderr_lines": [],
"stdout": " 06:12:38 up 26 days, 15:24, 1 user, load average: 0.43, 0.45, 0.60",
"stdout_lines": [
" 06:12:38 up 26 days, 15:24, 1 user, load average: 0.43, 0.45, 0.60"
]
}
]
}
}
PLAY RECAP ****************************************************************************************************
localhost : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
知道結果會是這樣以後,就可以再用 loop 來處理了。在上面的 playbook 最後加入以下 task,就可以只印出輸出結果了。
- name: Display only stdout
debug:
msg: "{{ item.stdout }}"
loop: "{{ shell_result.results }}"
loop_control:
label: "{{ item.stdout }}"
輸出結果如下(摘錄)
TASK [Display only stdout] ************************************************************************************
ok: [localhost] => (item=example-host) => {
"msg": "example-host"
}
ok: [localhost] => (item=西元2022年09月03日 (週六) 06時17分05秒 CST) => {
"msg": "西元2022年09月03日 (週六) 06時17分05秒 CST"
}
ok: [localhost] => (item= 06:17:05 up 26 days, 15:28, 1 user, load average: 0.47, 0.49, 0.58) => {
"msg": " 06:17:05 up 26 days, 15:28, 1 user, load average: 0.47, 0.49, 0.58"
}
這邊有使用到另外一個小技巧,避免輸出過多的資訊,就是 loop_control,這是在 2.2 以後所提供的語法。如果不使用,輸出結果會是像下面這樣子,可以看到,非常的冗長,導致不容易閱讀。
TASK [Display only stdout] ************************************************************************************
ok: [localhost] => (item={'cmd': 'hostname', 'stdout': 'example-host', 'stderr': '', 'rc': 0, 'start': '2022-09-03 06:21:09.487145', 'end': '2022-09-03 06:21:09.490120', 'delta': '0:00:00.002975', 'changed': True, 'invocation': {'module_args': {'_raw_params': 'hostname', '_uses_shell': True, 'warn': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['example-host'], 'stderr_lines': [], 'failed': False, 'item': 'hostname', 'ansible_loop_var': 'item'}) => {
"msg": "example-host"
}
ok: [localhost] => (item={'cmd': 'date', 'stdout': '西元2022年09月03日 (週六) 06時21分09秒 CST', 'stderr': '', 'rc': 0, 'start': '2022-09-03 06:21:09.627735', 'end': '2022-09-03 06:21:09.630569', 'delta': '0:00:00.002834', 'changed': True, 'invocation': {'module_args': {'_raw_params': 'date', '_uses_shell': True, 'warn': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': ['西元2022年09月03日 (週六) 06時21分09秒 CST'], 'stderr_lines': [], 'failed': False, 'item': 'date', 'ansible_loop_var': 'item'}) => {
"msg": "西元2022年09月03日 (週六) 06時21分09秒 CST"
}
ok: [localhost] => (item={'cmd': 'uptime', 'stdout': ' 06:21:09 up 26 days, 15:32, 1 user, load average: 0.53, 0.50, 0.55', 'stderr': '', 'rc': 0, 'start': '2022-09-03 06:21:09.769529', 'end': '2022-09-03 06:21:09.773419', 'delta': '0:00:00.003890', 'changed': True, 'invocation': {'module_args': {'_raw_params': 'uptime', '_uses_shell': True, 'warn': True, 'stdin_add_newline': True, 'strip_empty_ends': True, 'argv': None, 'chdir': None, 'executable': None, 'creates': None, 'removes': None, 'stdin': None}}, 'stdout_lines': [' 06:21:09 up 26 days, 15:32, 1 user, load average: 0.53, 0.50, 0.55'], 'stderr_lines': [], 'failed': False, 'item': 'uptime', 'ansible_loop_var': 'item'}) => {
"msg": " 06:21:09 up 26 days, 15:32, 1 user, load average: 0.53, 0.50, 0.55"
}
最後整理一下,這篇分享了兩個技巧:
- register 是可以搭配 loop 使用的,使用時,會多出一個 results 屬性,後面可以對 results 做處理就好。
- 可以使用 loop_control 來避免顯示過多資訊,提升可閱讀性。