我有几百个YAML文件需要不时更新。
更新前:
peekAll
更新后:
sss:
- ccc:
brr: 'mmm'
jdk: 'openjdk8'
- bbb:
brr: 'rel/bbb'
jdk: 'openjdk8'
- aaa:
brr: 'rel/aaa'
jdk: 'openjdk7'
sss: - ddd: brr: 'mmm' jdk: 'openjdk8' - ccc: brr: 'rel/ccc' jdk: 'openjdk8' - bbb: brr: 'rel/bbb' jdk: 'openjdk8' - aaa: brr: 'rel/aaa' jdk: 'openjdk7'
sss:
- ccc:
brr: 'mmm'
替换为'mmm'
:'rel/ccc'
- ccc: brr: 'rel/ccc'
- new: brr: 'new-mmm' jdk: 'openjdk8'
例如,我们需要更新上面的文件,看起来像保留每行中的空格/制表符,因为格式化对于YAML很重要。
我已经使用PyYAML尝试了这个,并且由于语法的复杂性而无法工作。可以通过使用awk,sed?
捕获空格来完成答案 0 :(得分:2)
尝试使用此类awk
程序:
/sss:/ { sss = 1; }
/- ccc:/ { ccc = 1; ind = substr($0, 1, index($0, "-")-1); next; } # don't print
$1 == "brr:" && $2 == "'mmm'" {
if (sss && ccc) {
print ind "- ddd:";
print ind " brr: 'mmm'";
print ind " jdk: 'openjdk8'";
print ind "- ccc:";
print ind " brr: 'rel/ccc'";
sss = 0; ccc = 0;
}
next;
}
{ print }
第一条规则用于标记输入sss
块,第二条规则用于标记ccc
块,另外还用于记录缩进深度。第三个规则添加新数据和修改后的数据,根据记录的深度缩进,然后退出sss
和ccc
块。最终规则打印刚刚读取的行。第二和第三条规则中的next
语句阻止应用所有后续规则。
答案 1 :(得分:1)
解析结构化数据,无论是YAML,HTML,XML还是CSV,仅使用正则表达式只能在一小部分可能情况下工作。使用YAML多行标量,以通用方式处理流式和块式等几乎是不可能的。如果不是这样的话,有人已经在awk中写了一个完整的YAML解析器。 (awk
没有任何问题,它不是处理YAML的正确工具。)
这并不意味着您不能使用正则表达式来查找特定元素,您只需要做一些准备:
import sys
import re
import ruamel.yaml
yaml_str = """\
sss:
- ccc:
brr: 'mmm'
jdk: 'openjdk8'
- bbb:
brr: 'rel/bbb'
jdk: 'openjdk8'
- aaa:
brr: 'rel/aaa'
jdk: 'openjdk7'
"""
class Paths:
def __init__(self, data, sep=':'):
self._sep = sep
self._data = data
def walk(self, data=None, prefix=None):
if data is None:
data = self._data
if prefix is None:
prefix = []
if isinstance(data, dict):
for idx, k in enumerate(data):
path_list = prefix + [k]
yield self._sep.join([str(q) for q in path_list]), path_list, idx, data[k]
for x in self.walk(data[k], path_list):
yield x
elif isinstance(data, list):
for idx, k in enumerate(data):
path_list = prefix + [idx]
yield self._sep.join([str(q) for q in path_list]), path_list, idx, k
for x in self.walk(k, path_list):
yield x
def set(self, pl, val):
pl = pl[:]
d = self._data
while(len(pl) > 1):
d = d[pl.pop(0)]
d[pl[0]] = val
def insert_in_list(self, pl, idx, val):
pl = pl[:]
d = self._data
while(len(pl) > 1):
d = d[pl.pop(0)]
d.insert(idx, val)
data = ruamel.yaml.round_trip_load(yaml_str, preserve_quotes=True)
paths = Paths(data)
pattern = re.compile('sss:.*:c.*:brr$')
# if you are going to insert/delete use list(paths.walk())
for p, pl, idx, val in list(paths.walk()):
print('path', p)
if not pattern.match(p):
continue
paths.set(pl, ruamel.yaml.scalarstring.SingleQuotedScalarString('rel/ccc'))
paths.insert_in_list(pl[:-2], idx, {'new': {
'brr': ruamel.yaml.scalarstring.SingleQuotedScalarString('mmm'),
'jdk': ruamel.yaml.scalarstring.SingleQuotedScalarString('openjdk8')
}})
print('----------')
ruamel.yaml.round_trip_dump(data, sys.stdout)
输出是:
path sss
path sss:0
path sss:0:ccc
path sss:0:ccc:brr
path sss:0:ccc:jdk
path sss:1
path sss:1:bbb
path sss:1:bbb:brr
path sss:1:bbb:jdk
path sss:2
path sss:2:aaa
path sss:2:aaa:brr
path sss:2:aaa:jdk
----------
sss:
- new:
brr: 'mmm'
jdk: 'openjdk8'
- ccc:
brr: 'rel/ccc'
jdk: 'openjdk8'
- bbb:
brr: 'rel/bbb'
jdk: 'openjdk8'
- aaa:
brr: 'rel/aaa'
jdk: 'openjdk7'
打印"路径"没有必要,但这里是为了更好地了解正在发生的事情。
SingleQuotedScalarString是获取YAML输出中字符串标量周围多余引号所必需的
由ruamel.yaml
加载YAML映射的dict子类支持Python 2.7和Python 3.5及更高版本的.insert(index, key, val)
,因此您也可以插入映射的特定位置