在Ansible/Jinja2中格式化项目列表



是否可以使用Ansible/Jinja2中的格式化字符串将列表/dicts列表转换为字符串列表?

我知道我可以做一些类似的事情:

{{["First: %d", "Second: %d"] | map("format", 1) | join(", ") }}

获取First: 1, Second 1

有可能做一些类似的事情吗

{{[[1, 1], [2, 2]] | map("format", "Num %d, %d") | join(", ") }}

并导致CCD_ 2?

  • 编号。这是不可能的。函数format的第一个参数必须是格式字符串。例如
- debug:
msg: "{{ ['v1 %s', 'v2 %s']|map('format', 'XYZ')|list }}"

给出

msg:
- v1 XYZ
- v2 XYZ

  • 相反,可以映射joinregex_replace项。例如,给定列表
_list: [[1, 1], [2, 2]]

下面的表达式

result: "{{ _list|
map('join', ',')|
map('regex_replace', _regex, _replace)|
join(', ') }}"
_regex: '^(.*),(.*)$'
_replace: 'Num 1, 2'

给出

result: Num 1, 1, Num 2, 2

  • 下一个选项是过滤器产品。下面的表达式给出了相同的结果
result: "{{ ['Num']|
product(_list|map('join', ', '))|
map('join', ' ')|
join(', ') }}"

  • 下一个选项是Jinja。下面的表达式给出了相同的结果
result: |-
{% for i in _list %}
Num {{ i|join(', ') }}{% if not loop.last %}, {% endif %}
{%- endfor %}

测试的完整剧本示例

- hosts: localhost
vars:
_list: [[1, 1], [2, 2]]
result1: "{{ ['Num']|
product(_list|map('join', ', '))|
map('join', ' ')|
join(', ') }}"
result2: "{{ _list|
map('join', ',')|
map('regex_replace', _regex, _replace)|
join(', ') }}"
_regex: '^(.*),(.*)$'
_replace: 'Num 1, 2'
result3: |-
{% for i in _list %}
Num {{ i|join(', ') }}{% if not loop.last %}, {% endif %}
{%- endfor %}
tasks:
- debug:
msg: "{{ ['v1 %s', 'v2 %s']|map('format', 'XYZ')|list }}"
- debug:
var: result1
- debug:
var: result2
- debug:
var: result3
- assert:
that:
- result1 == 'Num 1, 1, Num 2, 2'
- result2 == 'Num 1, 1, Num 2, 2'
- result3 == 'Num 1, 1, Num 2, 2'

使用核心format过滤器是不可能的。但如果你愿意写几行python,你可以用自定义过滤器轻松解决这个问题。我选择了最简单的演示,您可能需要强化代码并修复边缘案例,以便在现实生活中使用。您可能需要查看插件开发文档以了解更多信息。

对于本例,我将把自定义文件保存在测试剧本旁边的filter_plugins目录中。如果要在不同的项目中共享,可以将其保存在集合或角色中。

filter_plugins/my_format_filters.py


def reverse_format(param_list, format_string):
"""format format_sting using elements in param_list"""
return format_string % tuple(param_list)

class FilterModule(object):
"""my format filters."""
def filters(self):
"""Return the filter list."""
return {
'reverse_format': reverse_format
}

然后是以下示例剧本:

---
- name: custom filter demo
hosts: localhost
gather_facts: false
tasks:
- name: map custom reverse_format filter
debug:
msg: '{{ item.replacements | map("reverse_format", item.format) | join(", ") }}'
loop:
- replacements:
- [1, 1]
- [2, 2]
format: "Num %d, %d"
- replacements:
- ['Jack', 'John', 12]
- ['Mary', 'Alicia', 34]
format: "%s owes %s %d€"

给出:

PLAY [custom filter demo] **************************************************************************************************************************************************************************************************************
TASK [map custom reverse_format filter] ************************************************************************************************************************************************************************************************
ok: [localhost] => (item={'replacements': [[1, 1], [2, 2]], 'format': 'Num %d, %d'}) => {
"msg": "Num 1, 1, Num 2, 2"
}
ok: [localhost] => (item={'replacements': [['Jack', 'John', 12], ['Mary', 'Alicia', 34]], 'format': '%s owes %s %d€'}) => {
"msg": "Jack owes John 12€, Mary owes Alicia 34€"
}
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

在已经接受的答案的基础上,这里有一个版本也支持被馈送dicts/object列表:

class FilterModule(object):
"""my format filters."""
def filters(self):
"""Return the filter list."""
return {
'reverse_format': self.reverse_format
}
def reverse_format(self, value, format_string):
"""format format_sting using elements in param_list"""
return format_string.format(**value)

用法示例:

{{ hostvars.values() |
map('reverse_format', 'This is inventory host {inventory_hostname}!' | 
join('n') }}

最新更新