如何从Ansible字典中删除单个键?

18

我想在Ansible中从字典中删除一个键。

例如,我希望如下操作:

- debug: var=dict2
  vars:
    dict:
      a: 1
      b: 2
      c: 3
    dict2: "{{ dict | filter_to_remove_key('a') }}"

打印这个内容:

ok: [localhost] => {
    "dict2": {
        "b": 2,
        "c": 3
    }
}
请注意字典是从JSON文件加载的,我将其POST到Grafana REST API。我想在文件中允许保存“id”键并在POST之前删除该键。
这更接近我需要执行的实际用途:
- name: Install Dashboards   
  uri:
    url: "{{ grafana_api_url }}/dashboards/db"
    method: POST
    headers:
      Authorization: Bearer {{ grafana_api_token }}
    body:
      overwrite: true
      dashboard:
        "{{ lookup('file', item) | from_json | removekey('id') }}"
    body_format: json   with_fileglob:
    - "dashboards/*.json"
    - "../../../dashboards/*.json"

你为什么要创建一个新的字典?为什么不使用原始字典并在打印时跳过所需的键? - Shasha99
我猜你只能使用默认过滤器 dict | combine({'a':None}) 将键设置为 null,如果它符合你的需求。否则,你需要编写自定义过滤器插件。 - Konstantin Suvorov
@Shasha99 第二个字典只是为了帮助展示我需要的东西的类型。我实际上不需要打印字典;我需要将其POST到grafana。因此,我需要去掉一个键。我会在我的问题中尽量澄清。 - David Resnick
@KonstantinSuvorov 感谢您的建议,不过它确实会将一个键与值“null”一起留下。我将发布一个使用过滤器的解决方案,除非您想要。 - David Resnick
@KonstantinSuvorov...我刚刚注意到你意识到你的建议会留下一个“null”值 :) - David Resnick
@DavidResnick 如果是这样的话,最简单的方法就是使用Python创建一个新的Ansible模块,并根据您的需求使用它。如果您需要进一步的帮助,请告诉我。 - Shasha99
6个回答

14
- set_fact:
    dict:
      a: 1
      b: 2
      c: 3
    dict2: {}

- set_fact:
    dict2: "{{dict2 |combine({item.key: item.value})}}"
  when: "{{item.key not in ['a']}}"
  with_dict: "{{dict}}"

- debug: var=dict2

或者创建一个过滤器插件并使用它。


感谢您的指引。我创建了一个过滤器插件,但如果可能的话,最好使用本地的Ansible。 - David Resnick
非常好!你甚至不需要声明dict2,可以直接使用{}。 - Nikolaos Kakouros
更深层嵌套的键值怎么办? - stackprotector

7
您可以通过使用合并省略来实现此目的。
- set_fact:
    dict:
      a: 1
      b: 2
      c: 3

- debug:
    var: dict

- debug:
    msg: "{{ dict | combine({ 'a': omit }) }}"

TASK [test : set_fact] 
ok: [server]

TASK [test: debug] 
ok: [server] => {
    "dict": {
        "a": 1,
        "b": 2,
        "c": 3
    }
}

TASK [test : debug] 
ok: [server] => {
    "msg": {
        "b": 2,
        "c": 3
    }
}

5
  - debug: var=dict2
    vars:
      dict:
        a: 1
        b: 2
        c: 3
      dict2: '{{ dict | dict2items | rejectattr("key", "eq", "a") | list | items2dict }}'
      #dict2: '{{ dict | dict2items | rejectattr("key", "match", "^(a|b)$") | list | items2dict }}'

输出:

ok: [localhost] => {
    "dict2": {
        "b": 2,
        "c": 3
    }
}

不错的解决方案!可以将 ... | list | items2dict 缩短为 ... | items2dict(省略不必要的 | list)。 - Maxim Suslov

5

以下是一种灵感来源于John Mazzitelli的文章的方法,可以内联使用,无需额外的set_fact任务等:

任务:

tasks:
- debug: var=dict2
  vars:
    dict:
      a: 1
      b: 2
      c: 3
    # It is important that there be NO WHITESPACE outside of `{% ... %}` and `{{ ... }}`
    # or else the var will be converted to a string.  The copy() step optionally prevents
    # modifying the original. If you don't care, then: "{% set removed=dict.pop('a') %}{{dict}}"
    dict2: "{% set copy=dict.copy() %}{% set removed=copy.pop('a') %}{{ copy }}"

输出:

TASK [debug] ***********
ok: [localhost] => {
    "dict2": {
        "b": 2,
        "c": 3
    }
}

3

您可以使用ansible.utils.remove_keys 过滤器。它将删除所有嵌套层上给定的键。因此,在每种情况下可能都不适用:

- debug: var=dict2
  vars:
    dict:
      a: 1
      b: 2
      c: 3
    dict2: "{{ dict | ansible.utils.remove_keys(target=['a']) }}"

输出:

ok: [localhost] => 
  dict2:
    b: 2
    c: 3

如果您想在特定层中删除特定的键,可以使用以下方法将该键弹出:
- debug: var=dict2
  vars:
    dict:
      a: 1
      b: 2
      c: 3
    dict2: |
      {% set a = dict.pop('a') %}
      {{ dict }}

更深层次嵌套的示例:

- debug: var=dict2
  vars:
    dict:
      sub_dict:
        a: 1
        b: 2
        c: 3
    dict2: |
      {% set a = dict.sub_dict.pop('a') %}
      {{ dict }}

2
如果您对过滤器感兴趣(在我看来,这是从字典中删除项目的最清洁的方法),那么请在您的playbook所在的目录中创建filter_plugins/dicts.py文件,并填写以下内容: "最初的回答"。
'''Custom ansible filters for dicts'''

import copy


class FilterModule(object):

    def filters(self):
        return {
            'del_by_list': self.del_by_list
        }

    def del_by_list(self, dict_to_reduce, list_of_keys):
        '''Deletes items of dict by list of keys provided'''
        dict_to_return = copy.deepcopy(dict_to_reduce)
        for item in list_of_keys:
            if item in dict_to_return:
                del dict_to_return[item]
        return dict_to_return

最初的回答:然后你就可以开始了。
---
- hosts: hadoop
  gather_facts: no
  tasks:

    - debug:
        msg: "{{ {123: 456, 789: 123} | del_by_list([123]) }}"

这将产生{789: 123}的结果,即最初的回答。

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