我有一个看起来像这样的yaml文件:
user: John Doe
notes: {
"a": null,
"b": null,
"c": {
"title": "...",
"notes": [
{...},
{...}
]
}
}
tags: [
"tag1",
"tag2",
"tag3"
]
我要实现的是将整个块弄平,并用{}
和[]
包围,以便最后的输出看起来像这样:
user: John Doe
notes: {...}
tags: [...]
到目前为止,我设法做到的是:
sed ':a;N;$!ba;s/\n\s*//g' test.yml
但这仅通过删除所有新行就可以使整个Yaml内容平坦化。
有人会帮我吗?
谢谢。
注意:...
代表某些内容,在此上下文中并不那么重要。但是绝对必须在最后保留它。
注2:缩进既不确定内部({
,[
),也确定外部(}
,]
),但实际上可能有所不同。我发布的Yaml内容只是一个例子。内容也可能看起来像这样,请看一下左侧的输入是否是所需输出的右侧:https://jsfiddle.net/u7wbxn8d/2
注3:感谢@potong,这是我的问题的摘要:
解决方案必须为从行首开始并后跟:
的标记收集行,同时保留原始缩进。
(请参阅注释2中的示例。)
答案 0 :(得分:1)
您不应尝试使用诸如sed
之类的面向行的工具来尝试
更改一些与YAML一样复杂的内容。如果您有任何意见
更改,但仍然是有效的YAML,其基于正则表达式的模式
匹配可能会中断。
您可以使用YAML解析器轻松实现所需的功能,例如我的
ruamel.yaml
(适用于Python)。假设您的test.yaml
通过替换而更改为有效 YAML
{...}
的无效{answer: 42}
还有这个flatten.py
:
import sys
import ruamel.yaml
class MyRepresenter(ruamel.yaml.representer.RoundTripRepresenter):
def represent_none(self, data):
return self.represent_scalar(u'tag:yaml.org,2002:null', u'null', style='')
MyRepresenter.add_representer(type(None),
MyRepresenter.represent_none)
yaml = ruamel.yaml.YAML()
yaml.Representer = MyRepresenter
yaml.preserve_quotes = True
yaml.width = 4096 # line width before wrapping
data = yaml.load(sys.stdin)
yaml.dump(data, sys.stdout)
运行:
< test.yaml python flatten.py > out.yaml
给出一个out.yaml
:
user: John Doe
notes: {"a": null, "b": null, "c": {"title": "...", "notes": [answer: 42, answer: 42]}}
tags: ["tag1", "tag2", "tag3"]
您需要提供特殊的代表,因为null
通常是
表示为空标量(请参见
this的答案
详细信息)
答案 1 :(得分:0)
将块的行追加到模式空间的方法是可行的;我们只需要执行一个步骤即可。 e。从以[
或{
结尾的行到以]
或}
开头的行:
sed '/[[{]$/{:a;N;/\n[]}]/!ba;s/\n\s*//g}' test.yml
缩进是针对内部(
{
,[
)和外部(}
,]
)的不确定,实际上可能有所不同。
从您的小提琴中,我认为闭合]
或}
的缩进等于带有闭合[
或{
的行的缩进。基于此的awk
脚本的工作原理与sed
脚本相似,但另外,它通过搜索块的相同缩进的末端来处理缩进:
#!/usr/bin/awk -f
/[[{]$/ { match($0, /( *)/, space) # space[1] contains the indentation
do { printf "%s", $0; getline; t = $0; sub(/ */, "") }
while (!match(t, "^"space[1]"[]}]")) # search block end
}
{ print } # print simple line as well as last line of block
运行脚本
时出现此错误./awk.sh: line 2: syntax error at or near ,
您有一个awk
版本,它不支持第三个match()
参数。在这里,您可以将match($0, /( *)/, space)
替换为match($0, /( *)/); space[1] = substr($0, RSTART, RLENGTH)
。
如果右花括号/方括号不是从新行开始,则脚本将永远运行并且不会终止。
如果括号的放置位置不如上述脚本所假定的那样规则,则需要另一种方法。以下脚本仅计算括号嵌套级别,直到达到零为止。此外,如果文件包含未关闭的块,它将测试文件的末尾,以使其不会永远运行。
/[[{]$/ { level = 1 # bracket nesting level
do { printf "%s", $0; if (!getline) exit 1; sub(/ */, "")
level += gsub(/[[{]/, "&") - gsub(/[]}]/, "&")
} while (level) # search block end
}
{ print } # print simple line as well as last line of block
答案 2 :(得分:0)
这可能对您有用(GNU sed):
sed ':a;N;/\n\S\+:/!s/\n\s*//;ta;P;D' file
此解决方案为从行首开始并后跟:
的标记收集行。
答案 3 :(得分:0)
在gnu bash上尝试并sed:
放入您自己的内容
$ CONTENT='contents'
$ NOTE='notes'
sed -E "/^$NOTE/,/^\}/{/^notes/{s/.*/&$CONTENT\}/;b};d} ;/^tags/,/^\]/{/^tags/{s/.*/&$CONTENT\]/;b};d}" test.yml
在gnu awk上尝试过:
awk -vf=0 -vNOTE='notes' -vCONTENT='contents' '$0~/^NOTE/||f{f=1;print $0""CONTENT"}";while(getline&&$0!~/^\}/);f=0;next} $0~/^tags/||f{f=1;print $0""CONTENT"]";while(getline&&$0!~/^\]/);f=0;next}1' test.yml