首页 > 解决方案 > 单个项目和项目列表的循环和 with_items 行为之间的区别

问题描述

在这里看一个问题时,遇到了另一个问题。

让我们假设一个带有字典列表的变量,如下所示:

some_var:
  - k: key1
    m: value1
  - k: key2
    m: value2
  - k: key3
    m: value3

使用下面的代码循环some_var会按预期给出每个项目。

- debug:
    msg: "{{ item }}"
  loop: "{{ some_var | flatten(1) }}"

但是,当将循环变量放置为如下列表时,它不会循环遍历单个 dict 项,而是打印整个列表。

- debug:
    msg: "{{ item }}"
  loop: 
    - "{{ some_var | flatten(1) }}"

与在两种情况下都按预期工作loop相比,这种行为差异的原因是什么。with_items

标签: ansible

解决方案


正如 Ansible 文档所建议的那样,您在with_items和之间观察到的行为差异loop实际上是由于with_items隐含地为您展平列表的方式,这使得它比在您的 .loop

所以,这里的区别只在于列表被展平的那一刻:

  • 使用with_items,列表在 Ansible 内部被展平,所以它相当于:

    [ some_var ] | flatten(1)
    
  • loop您在 Ansible 的文档中看到它时,您必须自己展平列表,但是通过在上一级引入列表,您的展平发生得太早,因此相当于:

    [ some_var | flatten(1) ]
    

因此,如果您希望准确且明确地重现与循环中相同的行为with_items,则必须这样做:

- hosts: all
  gather_facts: no
  
  tasks:
    - debug:
        msg: "{{ item }}"
      loop: "{{ [ some_var | flatten(1) ] | flatten(1) }}"
      vars:
        some_var:
          - k: key1
            m: value1
          - k: key2
            m: value2
          - k: key3
            m: value3

正如预期的那样有效:

PLAY [all] **********************************************************************************************************

TASK [debug] ********************************************************************************************************
ok: [localhost] => (item={'k': 'key1', 'm': 'value1'}) => {
    "msg": {
        "k": "key1",
        "m": "value1"
    }
}
ok: [localhost] => (item={'k': 'key2', 'm': 'value2'}) => {
    "msg": {
        "k": "key2",
        "m": "value2"
    }
}
ok: [localhost] => (item={'k': 'key3', 'm': 'value3'}) => {
    "msg": {
        "k": "key3",
        "m": "value3"
    }
}

PLAY RECAP **********************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

这是因为您在第二个示例中创建了一个列表列表,而循环仅在列表的第一级上循环。

因此,您的第一个工作手册相当于在此处调试的变量上循环:

- hosts: all
  gather_facts: no
  
  tasks:
    - debug:
        msg: "{{ some_var | flatten(1) }}"
      vars:
        some_var:
          - k: key1
            m: value1
          - k: key2
            m: value2
          - k: key3
            m: value3

这给出了:

PLAY [all] **********************************************************************************************************

TASK [debug] ********************************************************************************************************
ok: [localhost] => {
    "msg": [
        {
            "k": "key1",
            "m": "value1"
        },
        {
            "k": "key2",
            "m": "value2"
        },
        {
            "k": "key3",
            "m": "value3"
        }
    ]
}

PLAY RECAP **********************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

但是,您的第二个示例等效于:

- hosts: all
  gather_facts: no
  
  tasks:
    - debug:
        msg: 
          - "{{ some_var | flatten(1) }}"
        ## Note that this above syntax is actually an equivalent to:
        # msg: "{{ [ some_var | flatten(1) ] }}"
      vars:
        some_var:
          - k: key1
            m: value1
          - k: key2
            m: value2
          - k: key3
            m: value3

这给出了:

PLAY [all] **********************************************************************************************************

TASK [debug] ********************************************************************************************************
ok: [localhost] => {
    "msg": [
        [
            {
                "k": "key1",
                "m": "value1"
            },
            {
                "k": "key2",
                "m": "value2"
            },
            {
                "k": "key3",
                "m": "value3"
            }
        ]
    ]
}

PLAY RECAP **********************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

看看在第二个例子中你是如何得到一个列表的?


所以,第一个例子的第一个元素是

{'k': 'key1', 'm': 'value1'}

如您所料,第二个元素的第一个元素只是一个包含所有元素的列表:

[   
    {
        "k": "key1",
        "m": "value1"
    },
    {
        "k": "key2",
        "m": "value2"
    },
    {
        "k": "key3",
        "m": "value3"
    }
]

推荐阅读