如何删除 YAML 文件中特定标头的所有特定子部分?

如何删除 YAML 文件中特定标头的所有特定子部分?

我正在使用 bash shell。我有一个 YAML 文件,我想从中删除某些文本块。

  /image-content:
    post:
      operationId: createEventPublic
      summary: Process events
      description: Process events
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Content'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Content'
  /text-content:
    post:
      operationId: createStaticText
      summary: Process text events
      description: Process text events
      parameters: []
      requestBody:
    ...

我想删除(作为示例)路径包含“图像内容”的文本块。通常我可以用它来删除包含该文本的单行

sed -i '/image-content/d' ./infile

但我不太清楚如何替换此后的每一行,直到下一行以两个空格和一个“/”(例如“/”)开头。在上面,我想删除所有内容,直到

  /text-content:

编辑:虽然这可能不是有效的 openapi 3 swagger,但我相信它仍然是有效的 YAML 文件

openapi: 3.0.0
components:
  /static/image-content:
    post:
      type: hello
  /api/hello:
    post:
      type: hello
  /static/css-content:
    post:
      type: hello

最终,我想删除以“/static”开头的块。所以结束文档是

openapi: 3.0.0
components:
  /api/hello:
    post:
      type: hello

答案1

yq -y 'del(."/image-content")' file.yml

这使用yq来自https://kislyuk.github.io/yq//image-content使用以下命令从 YAML 文档中删除顶级部分del()

鉴于问题中的示例文档,按原样,这将导致以下 YAML 文档被写入终端:

/text-content:
  post:
    operationId: createStaticText
    summary: Process text events
    description: Process text events
    parameters: []
    requestBody: null

如果您想保存它,请将其重定向到新文件,或者使用该--in-place选项进行就地编辑(当然,首先在没有该选项的情况下进行测试之后)。

yq是 JSON 解析器的包装器jq,允许使用jq表达式来处理 YAML 文件。


如果问题中的文档是部分的并且没有显示其真实结构(额外的两个缩进空格意味着我们看到的是二级级别的部分),那么您可能需要使用

yq -y 'del(.[]."/image-content")' file.yml

.[]."/image-content"表达是指“/image-content顶层以下的任何部分”。

递归地搜索并删除/image-content部分,无论它们可能出现在文档中的哪个位置,请使用

yq -y 'del(.. | ."/image-content"?)' file.yml

中使用的表达式del()递归地遍历文档结构,..并提取任何名为 的部分/image-content,其中有一个(这对应于//XPath 查询中的运算符)。然后将这些内容删除。


解决您更新的问题:

yq -y '.components |= with_entries(del(select(.key | startswith("/static/"))) // empty)' file.yml

这会components通过获取其子部分、暂时将它们转换为单独的keyvalue值(请参阅手册with_entries()中的文档jq)、选择并删除键以确切的 string 开头的部分来更新该部分/static/

// emptydel()运算结果为null值。这些无法从key值中转回value适当的小节,因此我将它们更改为empty值,这使它们完全消失。老实说,我并不完全确定这件事的内部运作方式。

这导致

openapi: 3.0.0
components:
  /api/hello:
    post:
      type: hello

答案2

测试用GNU sed

sed -n '
    /^\s*\/static/ {
        n
        :c
            /^[[:space:]]*\//! {
                n
                bc
            }
        }
    p
' data

那么对于第二个问题基本是一样的:

sed -n '
    /^[[:space:]]\+\/image-content:$/ {
        n
        :c
            /^[[:space:]]\+\//! {
                n
                bc
            }
    }
    p
' data

第一行查找所需的段落,然后循环并删除其每一行,直到找到新段落。当然,您可以插入-i用于就地编辑的标志。

答案3

通用解决方案:删除匹配行以及所有缩进较多的行

如果您有给定格式的文件,通常最好使用专为该格式设计的工具。在您的情况下,您似乎有一个基于行缩进的空格的简单规则,所以为什么不为标准工具提供一个简单的脚本:

sed -e 'H;x;/^\(  *\)\n\1/{s/\n.*//;x;d;}' -e 's/.*//;x;/\/image-content/{s/^\( *\).*/ \1/;x;d;}' file

它的作用:如果找到具有匹配模式的行,则将其删除,同时将空格数保存在保留空间中,并附加一个空格。然后,对于每一行,检查它是否以至少与保持缓冲区一样多的空格开头;如果是,也将其删除,直到缩进较少的行重置保留空间。

详细描述

  • H;x将当前行附加到H旧空间并交换空间,因此现在当前行保存在保留空间中,而在模式空间中我们可以检查附加到旧保留空间的行
  • /^\( *\)\n\1/是一种模式,用于识别保留空间中至少有一个空格,并且当前行至少具有与保留缓冲区一样多的空格。这意味着我们需要删除这些行,并且这些{}行仅在这种情况下执行:
  • s/\n.*//清除从换行符开始的所有内容,因此我们删除附加行并恢复之前保留缓冲区中的内容。现在我们可以x再次更改缓冲区以返回到旧状态并d删除当前模式空间以开始新的循环
  • 仅当未删除任何行时才会执行脚本的其余部分。s/.*//;x清除模式空间并交换空间,因此我们处于初始状态:当前行在模式空间中,而保留空间为空
  • 最后,我们需要删除一个section的触发器:\/image-content可以是任意的触发模式,当然也可以是\/static并且可以是在任何缩进级别。因此,此后的所有操作都只会针对触发线执行。所有其他行将被简单地打印。
  • s/^\( *\).*/ \1/;x取出这一行中的所有空格,添加另一个空格并将其放置在保留空间中以供将来比较(我们在脚本开头所做的)。那么当然我们需要d删除以避免任何输出。

答案4

  • 没有评论
sed '/^ *\/image-content:/{
:sub;
  $b eof;
  N;
/^\( *\)[^ ].*\n\1[^ ][^\n]*$/!b sub;
:eof;
  s/^\( *\)[^ ].*\n\(\1[^ ][^\n]*\)$/\2/;
  t loop;
  d;
:loop; n; b loop;
}' file;
  • 有评论
sed '/^ *\/image-content:/{
:sub;
  $b eof;  # end of file
  N;
/^\( *\)[^ ].*\n\1[^ ][^\n]*$/!b sub;  # leading-spaces==ending-spaces(\1). loop if not same level
:eof;
  # if join with the first-line of next block, only leave the joint-line.
  s/^\( *\)[^ ].*\n\(\1[^ ][^\n]*\)$/\2/;
  t loop;  # jump if s/././ is done
  d;  # no more lines after target block
:loop; n; b loop;  # b loop is to speed the process
}' file;

相关内容