是否可以使用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
- 相反,可以映射join和regex_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') }}