Jinja2: 格式化+拼接列表中的元素

26

play_hosts是一个玩耍中使用的所有机器列表。我想将它们使用类似format()的东西重新编写,比如rabbitmq@%s,然后使用类似join()的方法将它们连接在一起。所以:

{{ play_hosts|format(???)|join(', ') }}

所有的格式示例都使用管道,其中输入是格式字符串而不是列表。有没有办法使用这些示例(或其他东西)来完成我想要的事情?输出应该看起来像:

['rabbitmq@server1', 'rabbitmq@server2', rabbitmq@server3', ...]

jinja2文档描述格式如下:

format(value, *args, **kwargs)

在对象上应用Python字符串格式化:

{{ "%s - %s"|format("Hello?", "Foo!") }}
-> Hello? - Foo!

它提供了三种输入,但在示例中没有描述这些输入,示例只显示一个通过管道传递的输入和另外两个通过参数传递的输入。是否有关键字参数来指定管道中的字符串?请帮忙,Python专家们!


这段代码能用吗?(我从未使用过jinja){% for host in play_hosts %} {{ "rabbitmq@%s"|format(host) }} {% endfor %} - Pynchia
似乎列表推导式就是你所需要的。 - Barmar
@Barmar,Jinja2不支持列表推导式。 - DylanYoung
5个回答

30
在ansible中,你可以使用regex_replace过滤器:
{{ play_hosts | map('regex_replace', '^(.*)$', 'rabbitmq@\\1') | list }}

2
简化版:{{ play_hosts | map('regex_replace', '^', 'rabbitmq@') | list }} - Jiří Baum

14

我认为另一种方法是使用全局函数joiner,你可以在http://jinja.pocoo.org/docs/2.9/templates/#list-of-global-functions中阅读到相关内容:

传递一个字符串给一个joiner函数,并且每次调用该函数时都会返回该字符串(第一次调用时返回空字符串)。你可以使用它来拼接内容。

因此,你的代码应该如下所示:

[
{% set comma = joiner(",") %}    
{% for host in play_hosts %}
    {{ comma() }}
    {{ "rabbitmq@%s"|format(host) }}
{% endfor %}
]

13
你可以创建自定义过滤器。
# /usr/share/ansible/plugins/filter/format_list.py (check filter_plugins path in ansible.cfg)

def format_list(list_, pattern):
    return [pattern % s for s in list_]


class FilterModule(object):
    def filters(self):
        return {
            'format_list': format_list,
        }

并使用它

{{ play_hosts | format_list('rabbitmq@%s') }}

我认为这是最好的解决方案。如果您尝试在playbook中完成所有操作,您的Jinja2代码将很快变得难以管理。 - rumdrums
这样的format_list应该真的是Jinja2的一部分! - geekQ

10
您不仅可以通过逗号,简单地join,还可以将前缀与其一起添加。尽管这不是非常符合Python风格或高级,但这是一个非常简单有效的解决方案:
[rabbitmq@{{ play_hosts | join(', rabbitmq@') }}]

重复不好看,但比regex_replace更简洁。在jinja2中真的需要一个干净的过滤器。为解决方法点个赞。 - geekQ
4
如果列表为空,这将导致一个悬空的 rabbitmq@ - lmNt

-2

如果你想通过列表格式化一个字符串。

l: list = ["world", "stackoverflow"]  
"Hello %s and %s"|format(*l)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接