Удаление элемента в файле yaml - PullRequest
0 голосов
/ 01 мая 2019

Мой файл 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 ]

1 голос
/ 01 мая 2019

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

Будут сохранены такие вещи, как комментарии, скаляры в кавычках, специальный целочисленный стиль (шестнадцатеричный, восьмеричный, двоичный), ахоры / псевдонимы, объединенное отображение.

1 голос
/ 01 мая 2019

Не используя библиотеку 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

Однако это не является надежным в отношении таких вещей, как «Я хочуIP-адреса, в которых нет «2», так как проверка 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])

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...