删除Yaml文件中的元素

时间:2019-05-01 16:15:12

标签: python 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文件。

2 个答案:

答案 0 :(得分:1)

不使用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])

答案 1 :(得分:1)

在迭代时更改数据结构时,Python不喜欢它,因此在加载数据结构后,您必须先通过以确定要删除的项目,然后再进行一次删除它们。为了不打乱索引,从头到尾进行了删除。

假设您的示例在input.yaml

import sys
from pathlib import Path
import ruamel.yaml

yaml_file = Path('input.yaml')

yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True
yaml.explicit_start = True
data = yaml.load(yaml_file)

to_remove = []
for idx, item in enumerate(data):
    if item['hostname'] == 'core-sw-tor':
       to_remove.insert(0, idx)  # creates a reversed list
for idx in to_remove:
    del data[idx]
yaml.dump(data, yaml_file)

给出input.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

将保留注释,带引号的标量,特殊的整数样式(十六进制,八进制,二进制),achors /别名,合并映射之类的内容。