当块内匹配模式时删除两个正则表达式标记之间的块
Deleting the block between two regex markers when a pattern is matched inside the block
让我们假设以下结构:
- key1: value11
key2:
- value21
- value22
- value23
key3: value31
key4:
- value41
- value42
key5: value51
- key1: value12
key2:
- value24
- value25
key3: value32
key5: value52
- key1: value13
key2:
- value26
key3: value33
key4:
- value43
- value44
- value45
key5: value53
是否可以删除开始和结束标记正则表达式之间(包括在内)的所有块:
- begin marker: '^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$'
- end marker: '^[[:blank:]]{6}key5:[[:blank:]].+$'
当以下正则表达式在块内匹配时:
matching pattern: '^[[:blank:]]{6}key3:[[:blank:]]value32$'?
目标是获得:
- key1: value11
key2:
- value21
- value22
- value23
key3: value31
key4:
- value41
- value42
key5: value51
- key1: value13
key2:
- value26
key3: value33
key4:
- value43
- value44
- value45
key5: value53
开始标记也可以作为结束标记,因为第二个标记出现在块移除期间没有被删除。
我尝试了多种 sed/awk 的方法,但都没有成功,例如这个 post:
中灵感来自 4.21 段落的方法
sed ':t
/^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/,/^[[:blank:]]{6}key5:[[:blank:]].+$/ { # For each line between these block markers
/^[[:blank:]]{6}key5:[[:blank:]].+$/!{ # If we are not at the /end/ marker
$!{ # nor the last line of the file
N; # add the Next line to the pattern space
bt
} # and branch (loop back) to the :t label
} # This line matches the /end/ marker
/^[[:blank:]]{6}key3:[[:blank:]]value32$/d; # If /regex/ matches, delete the block
}' file
sed 是对单个字符串执行 s/old/new/
的正确工具,仅此而已。对于任何更有趣的事情,您应该使用 awk 来获得清晰度、可移植性、健壮性、效率等。
根据您发布的示例 input/output,您实际上不需要指定的第一个正则表达式,例如使用 GNU awk 进行多字符 RS 和 RT:
awk -v RS='[[:blank:]]{6}key5:[[:blank:]][^\n]+\n' -v ORS= '
!/\n[[:blank:]]{6}key3:[[:blank:]]value32\n/{ print [=10=] RT }
' file
- key1: value11
key2:
- value21
- value22
- value23
key3: value31
key4:
- value41
- value42
key5: value51
- key1: value13
key2:
- value26
key3: value33
key4:
- value43
- value44
- value45
key5: value53
或使用任何 awk:
awk '
{ rec = rec [=11=] ORS }
/^[[:blank:]]{6}key5:[[:blank:]].+$/ {
if ( rec !~ /\n[[:blank:]]{6}key3:[[:blank:]]value32\n/ ) {
printf "%s", rec
}
rec=""
}
' file
- key1: value11
key2:
- value21
- value22
- value23
key3: value31
key4:
- value41
- value42
key5: value51
- key1: value13
key2:
- value26
key3: value33
key4:
- value43
- value44
- value45
key5: value53
但如果您愿意,您也可以使用第一个正则表达式,例如:
awk '
/^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/ { inBlock=1 }
inBlock { rec = rec [=12=] ORS }
/^[[:blank:]]{6}key5:[[:blank:]].+$/ {
if ( rec !~ /\n[[:blank:]]{6}key3:[[:blank:]]value32\n/ ) {
printf "%s", rec
}
rec=""
inBlock=0
}
' file
- key1: value11
key2:
- value21
- value22
- value23
key3: value31
key4:
- value41
- value42
key5: value51
- key1: value13
key2:
- value26
key3: value33
key4:
- value43
- value44
- value45
key5: value53
如果你真的想要 sed,你可以将范围存储在 hold space 中,然后打印 hold space 当且仅当它不包含你想要排除的字符串时整个范围:
/^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/,/^[[:blank:]]{6}key5:[[:blank:]].+$/{
/^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/h
//!H
/^[[:blank:]]{6}key5:[[:blank:]].+$/{
g
/\n[[:blank:]]{6}key3:[[:blank:]]value32\n/!p
}
d
}
以上必须是运行和sed -Ef cmdfile file
。
对此的几个烦恼之一是必须重复模式。
文件格式看起来像 YAML。那为什么不用yq
过滤呢?
然后你可以说:
yq -y '[ .[] | select (.key3 != "value32") ]' file
结果:
- key1: value11
key2:
- value21
- value22
- value23
key3: value31
key4:
- value41
- value42
key5: value51
- key1: value13
key2:
- value26
key3: value33
key4:
- value43
- value44
- value45
key5: value53
您可能需要安装 yq
和 pip install yq
或类似的东西。
这可能适合您 (GNU sed):
sed -E '/^\s{2}-\s{3}key1:\s/{:a;N;/^\s{6}key5:\s/M!ba;/^\s{6}key3:\svalue32$/Md}' file
收集 key1
和 key5
之间的一组行,如果该组包含所需的字符串,则删除整个组。
N.B。使用 M
标志,允许多行匹配。
本质上:
sed '/key1/{:a;N;/key5/!ba;/key3.*value32$/Md}' file
让我们假设以下结构:
- key1: value11
key2:
- value21
- value22
- value23
key3: value31
key4:
- value41
- value42
key5: value51
- key1: value12
key2:
- value24
- value25
key3: value32
key5: value52
- key1: value13
key2:
- value26
key3: value33
key4:
- value43
- value44
- value45
key5: value53
是否可以删除开始和结束标记正则表达式之间(包括在内)的所有块:
- begin marker: '^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$'
- end marker: '^[[:blank:]]{6}key5:[[:blank:]].+$'
当以下正则表达式在块内匹配时:
matching pattern: '^[[:blank:]]{6}key3:[[:blank:]]value32$'?
目标是获得:
- key1: value11
key2:
- value21
- value22
- value23
key3: value31
key4:
- value41
- value42
key5: value51
- key1: value13
key2:
- value26
key3: value33
key4:
- value43
- value44
- value45
key5: value53
开始标记也可以作为结束标记,因为第二个标记出现在块移除期间没有被删除。
我尝试了多种 sed/awk 的方法,但都没有成功,例如这个 post:
中灵感来自 4.21 段落的方法sed ':t
/^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/,/^[[:blank:]]{6}key5:[[:blank:]].+$/ { # For each line between these block markers
/^[[:blank:]]{6}key5:[[:blank:]].+$/!{ # If we are not at the /end/ marker
$!{ # nor the last line of the file
N; # add the Next line to the pattern space
bt
} # and branch (loop back) to the :t label
} # This line matches the /end/ marker
/^[[:blank:]]{6}key3:[[:blank:]]value32$/d; # If /regex/ matches, delete the block
}' file
sed 是对单个字符串执行 s/old/new/
的正确工具,仅此而已。对于任何更有趣的事情,您应该使用 awk 来获得清晰度、可移植性、健壮性、效率等。
根据您发布的示例 input/output,您实际上不需要指定的第一个正则表达式,例如使用 GNU awk 进行多字符 RS 和 RT:
awk -v RS='[[:blank:]]{6}key5:[[:blank:]][^\n]+\n' -v ORS= '
!/\n[[:blank:]]{6}key3:[[:blank:]]value32\n/{ print [=10=] RT }
' file
- key1: value11
key2:
- value21
- value22
- value23
key3: value31
key4:
- value41
- value42
key5: value51
- key1: value13
key2:
- value26
key3: value33
key4:
- value43
- value44
- value45
key5: value53
或使用任何 awk:
awk '
{ rec = rec [=11=] ORS }
/^[[:blank:]]{6}key5:[[:blank:]].+$/ {
if ( rec !~ /\n[[:blank:]]{6}key3:[[:blank:]]value32\n/ ) {
printf "%s", rec
}
rec=""
}
' file
- key1: value11
key2:
- value21
- value22
- value23
key3: value31
key4:
- value41
- value42
key5: value51
- key1: value13
key2:
- value26
key3: value33
key4:
- value43
- value44
- value45
key5: value53
但如果您愿意,您也可以使用第一个正则表达式,例如:
awk '
/^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/ { inBlock=1 }
inBlock { rec = rec [=12=] ORS }
/^[[:blank:]]{6}key5:[[:blank:]].+$/ {
if ( rec !~ /\n[[:blank:]]{6}key3:[[:blank:]]value32\n/ ) {
printf "%s", rec
}
rec=""
inBlock=0
}
' file
- key1: value11
key2:
- value21
- value22
- value23
key3: value31
key4:
- value41
- value42
key5: value51
- key1: value13
key2:
- value26
key3: value33
key4:
- value43
- value44
- value45
key5: value53
如果你真的想要 sed,你可以将范围存储在 hold space 中,然后打印 hold space 当且仅当它不包含你想要排除的字符串时整个范围:
/^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/,/^[[:blank:]]{6}key5:[[:blank:]].+$/{
/^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/h
//!H
/^[[:blank:]]{6}key5:[[:blank:]].+$/{
g
/\n[[:blank:]]{6}key3:[[:blank:]]value32\n/!p
}
d
}
以上必须是运行和sed -Ef cmdfile file
。
对此的几个烦恼之一是必须重复模式。
文件格式看起来像 YAML。那为什么不用yq
过滤呢?
然后你可以说:
yq -y '[ .[] | select (.key3 != "value32") ]' file
结果:
- key1: value11
key2:
- value21
- value22
- value23
key3: value31
key4:
- value41
- value42
key5: value51
- key1: value13
key2:
- value26
key3: value33
key4:
- value43
- value44
- value45
key5: value53
您可能需要安装 yq
和 pip install yq
或类似的东西。
这可能适合您 (GNU sed):
sed -E '/^\s{2}-\s{3}key1:\s/{:a;N;/^\s{6}key5:\s/M!ba;/^\s{6}key3:\svalue32$/Md}' file
收集 key1
和 key5
之间的一组行,如果该组包含所需的字符串,则删除整个组。
N.B。使用 M
标志,允许多行匹配。
本质上:
sed '/key1/{:a;N;/key5/!ba;/key3.*value32$/Md}' file