【问题标题】:Is it possible to flatten a lists of lists with Ansible / Jinja2?是否可以使用 Ansible / Jinja2 展平列表列表?
【发布时间】:2026-02-09 23:00:01
【问题描述】:

我的基本问题是,在创建一组 aws 服务器后,我想将它们配置为相互了解。

创建每个服务器后,它们的详细信息都会保存在已注册的“服务器”变量中(如下所示)。创建后我真正想做的是运行这样的任务:

- name: Add servers details to all other servers
  lineinfile:
    dest: /path/to/configfile
    line: "servername={{ item.1.private_ip }}"
  delegate_to: "{{ item.0.public_dns_name }}"
  with_nested:
    - list_of_servers
    - list_of_servers

这里必须将列表两次提供给“with_nested”。

获取列表很容易做到:

"{{ servers.results | map(attribute='tagged_instances') | list }}"

返回:

[ 
  [ { "private_ip": "ip1", "public_dns_name": "dns1" } , { ... }],
  [ { ... }, { ... } ]
]

但是你会怎么把它变成:

[
  { "private_ip": "ip1", "public_dns_name": "dns1" },
  { ... },
  { ... }, 
  { ... }
]

“服务器”注册的变量如下所示:

"servers": {
    "changed": true,
    "msg": "All items completed",
    "results": [
        {
            ...
            "tagged_instances": [
                {
                    ...
                    "private_ip": "ip1",
                    "public_dns_name": "dns1",
                    ...
                },
                {
                    ...
                    "private_ip": "ip2",
                    "public_dns_name": "dns2",
                    ...
                }
            ]
        },
        {
            ...
            "tagged_instances": [
                {
                    ...
                    "private_ip": "ip3",
                    "public_dns_name": "dn3",
                    ...
                },
                {
                    ...
                    "private_ip": "ip4",
                    "public_dns_name": "dns4",
                    ...
                }
            ]
        },
        ...
    ]
}

注意:我有一个非常丑陋的解决方案,它使用“with_flattened”和一个调试语句来创建一个新的注册变量“flattened_servers”,然后我再次映射它。但我希望有一个更优雅的解决方案:)

【问题讨论】:

    标签: jinja2 ansible ansible-playbook


    【解决方案1】:

    有点晚了,但是从 ansible 2.5 开始你可以这样做:

     "{{ servers.results | map(attribute='tagged_instances') | list | flatten }}"
    

    【讨论】:

    • 干净多了,谢谢!我发现| flatten 甚至可以在没有| list 的发电机上工作
    【解决方案2】:

    您可以进行列表推导以将其转换为字典列表。

    例如:

    - name: Convert
      shell: python -c "print [x for b in {{ servers }}['servers']['results'] for x in b['tagged_instances']]"
      register: my_list_of_dicts
    

    假设 {{ servers }} 变量包含整个字典(不是 json)。

    【讨论】:

    • 这绝对是在 yml 中运行循环的一大改进!看看是否有人知道如何在 jinja2 中进行列表理解。否则这很可能是答案。
    • Jinja 官方不支持列表解析或其他“高级”Python 操作:jinja.pocoo.org/docs/dev/faq/…。我在 Google 上搜索过“Jinja2 列表理解”太多次了,以至于忘记了。 :(
    【解决方案3】:

    Jinja2 带有一个内置过滤器sum,可以像这样使用:

    {{ servers.results | sum(attribute='tagged_instances', start=[]) }}
    

    【讨论】:

    • 如果您需要获取所有实例 ID,您可以像这样扩展上面的示例:code - set_fact: instance_ids="{{ servers.results | sum(attribute='tagged_instances', start =[]) | 地图(attribute='id') | 列表 }}"
    • 我发现这是最“可靠”的答案,而且效果很好。
    • 这也适用于数组数组!我的数据看起来像 [ [1, 2], [3, 4] ] 并使用 | sum(start = []) 将其展平为 [1, 2, 3, 4]。非常感谢 - 永远不会想到这一点。
    【解决方案4】:

    只是想列出“丑陋”的解决方法,因为 python 代码对我不起作用

      - debug: var=item
        with_flattened:
          - "{{ servers.results|map(attribute='tagged_instances')|list }}"
        register: servers_instances_tmp
        no_log: True
    
      - set_fact: servers_instances="{{ servers_instances_tmp.results|map(attribute='item')|list }}"
    
      - debug: var=servers_instances
    

    【讨论】:

      最近更新 更多