首页 > 解决方案 > 删除 yaml 文件中的元素

问题描述

我的 yaml 文件是这样构造的:

---
- hostname: core-fw-tor
  ip: 1.1.1.1
  os: ios
  password: password
  platform: cisco
  type: ios
  username: admin
- hostname: core-rt-tor
  ip: 2.2.2.2
  os: ios
  password: password
  platform: cisco
  type: ios
  username: admin
- hostname: core-sw-tor
  ip: 3.3.3.3
  os: ios
  password: password
  platform: cisco
  type: ios
  username: admin

如何从此列表中删除/移除元素?例如,用户想要删除主机名为“core-sw-tor”的元素并更新 yaml 文件。

标签: pythonyaml

解决方案


如果不使用该yaml库,您可以执行以下操作:

import re

# read the yaml file like you would any other
with open('somefile.yaml') as fh:
    content = fh.read()

# the - is your delimiter in yaml, with spaces surrounding it
recs = [x for x in re.split('\s-\s', content)]

recs
# ['---', 'hostname: core-fw-tor\n  ip: 1.1.1.1\n  os: ios\n  password: password\n  platform: cisco\n  type: ios\n  username: admin', 'hostname: core-rt-tor\n  ip: 2.2.2.2\n  os: ios\n  password: password\n  platform: cisco\n  type: ios\n  username: admin', 'hostname: core-sw-tor\n  ip: 3.3.3.3\n  os: ios\n  password: password\n  platform: cisco\n  type: ios\n  username: admin']

# To get everything *except* that entry
kept = [r for r in recs if 'core-sw-tor' not in r]

# ['---', 'hostname: core-fw-tor\n  ip: 1.1.1.1\n  os: ios\n  password: password\n  platform: cisco\n  type: ios\n  username: admin', 'hostname: core-rt-tor\n  ip: 2.2.2.2\n  os: ios\n  password: password\n  platform: cisco\n  type: ios\n  username: admin']

# Write to a new file by joining back all of the records
with open('newyaml.yaml', 'w') as fh:
    fh.write('\n- '.join(kept))

哪个会输出

---
- hostname: core-fw-tor
  ip: 1.1.1.1
  os: ios
  password: password
  platform: cisco
  type: ios
  username: admin
- hostname: core-rt-tor
  ip: 2.2.2.2
  os: ios
  password: password
  platform: cisco
  type: ios
  username: admin

但是,这对于诸如“我想要其中没有 '2' 的 ip 地址”之类的内容并不可靠,因为签2入字符串内容可能会删除比您想要的更多的内容。将所有内容放入字典可能是更好的方法

# Starting from recs on the previous snippet
# you don't want ---, so we can just remove it with unpacking
top, *recs = recs

# I'm going to do this in a traditional for loop for visibility's sake
# use a set for fast membership tests on larger files
entries = set()

for i,rec in enumerate(recs):
    # collect entry into a dictionary
    d = {k.strip(): v.strip() for k,v in [x.split(':') for x in rec.split('\n')]}
    # {'hostname': 'core-sw-tor', 'ip': '3.3.3.3', 'os': 'ios', 'password': 'password', 'platform': 'cisco', 'type': 'ios', 'username': 'admin'}
    # as an example

    # You can change the key and value here
    if d.get('hostname') != 'core-sw-tor':
        entries.add(i)

kept = [x for i, x in enumerate(recs) if in entries]

with open('new_yaml.yaml', 'w') as fh:
    total_rec = [top, *kept]
    fh.write('\n- '.join(total_rec))

哪个输出相同并且更强大

作为一个完整的功能,您可能会实际使用

import re

def read_yaml(path, header=True):
    with open(path) as fh:
        content = fh.read()

    if header:
        top, *s = re.split('\s-\s', content)
    else:
        top, s = '', re.split('\s-\s', content)

    return top, s

# find by key and value, not_match implies find everything that
# *isn't* some value
def find_entries(records, key, val, not_match=True):
    entries = set()
    for i, rec in enumerate(records):
        d = {k.strip(): v.strip() for k,v in [x.split(':') for x in rec.split('\n')]}
        if not_match is True:
            if d.get(key) != val:
                entries.add(i)
        else:
            if d.get(key) == val:
                entries.add(i)

    return entries

## Now it's just like before
top, recs = read_yaml('someyaml.yaml')

# Now you can change this to be 'ip' and '2.2.2.2', if you wanted
ents = find_entries(recs, 'hostname', 'core-sw-tor')

kept = [x for x in recs if i in ents]

with open('newyaml', 'w') as fh:
    fh.write('\n- '.join([top, *kept])


推荐阅读