我正在使用 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
通过获取其子部分、暂时将它们转换为单独的key
和value
值(请参阅手册with_entries()
中的文档jq
)、选择并删除键以确切的 string 开头的部分来更新该部分/static/
。
位// empty
:del()
运算结果为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;