删除仅针对特定块的新行

时间:2019-04-25 11:27:59

标签: regex bash sed yaml

我有一个看起来像这样的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中的示例。)

4 个答案:

答案 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