Awk 当模式在块内匹配时,删除两个正则表达式标记之间的块

Awk 当模式在块内匹配时,删除两个正则表达式标记之间的块,awk,sed,pattern-matching,block,markers,Awk,Sed,Pattern Matching,Block,Markers,让我们假设以下结构: - key1: value11 key2: - value21 - value22 - value23 key3: value31 key4: - value41 - value42 key5: value51 - key1: value12 key2: - value24 - value25 key3:

让我们假设以下结构:

  -   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:]].+$'
当以下正则表达式在块内匹配时:

目标是获得:

  -   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中尝试了多种方法,但都没有成功,例如下面4.21段中的一种方法:


sed是对单个字符串执行
s/old/new/
的正确工具,仅此而已。对于任何更有趣的事情,为了清晰、可移植、健壮、高效等,您应该使用awk

如果您发布了示例输入/输出,您实际上不需要指定的第一个regexp,例如,对于多字符RS和RT,使用GNU awk:

awk -v RS='[[:blank:]]{6}key5:[[:blank:]][^\n]+\n' -v ORS= '
    !/\n[[:blank:]]{6}key3:[[:blank:]]value32\n/{ print $0 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 $0 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
但如果您愿意,也可以使用第一个regexp,例如:

awk '
/^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/ { inBlock=1 }
inBlock { rec = rec $0 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是对单个字符串执行
s/old/new/
的正确工具,仅此而已。对于任何更有趣的事情,为了清晰、可移植、健壮、高效等,您应该使用awk

如果您发布了示例输入/输出,您实际上不需要指定的第一个regexp,例如,对于多字符RS和RT,使用GNU awk:

awk -v RS='[[:blank:]]{6}key5:[[:blank:]][^\n]+\n' -v ORS= '
    !/\n[[:blank:]]{6}key3:[[:blank:]]value32\n/{ print $0 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 $0 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
但如果您愿意,也可以使用第一个regexp,例如:

awk '
/^[[:blank:]]{2}-[[:blank:]]{3}key1:[[:blank:]].+$/ { inBlock=1 }
inBlock { rec = rec $0 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,则可以将范围存储在保留空间中,然后仅当保留空间不包含要排除整个范围的字符串时才打印保留空间:

/^[[: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文件运行


这样做的几个麻烦之一是必须重复模式。

如果确实需要sed,可以将范围存储在保留空间中,然后仅当保留空间不包含要排除整个范围的字符串时才打印保留空间:

/^[[: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文件运行


其中一个烦恼是必须重复这些模式。

文件格式看起来像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

您可能需要使用
pip install yq
或类似工具安装
yq

文件格式看起来像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
您可能需要使用
pip install yq
或类似工具安装
yq

这可能适合您(GNU-sed):

key1
key5
之间收集一组行,如果该组包含所需字符串,则删除整个组

注意:使用允许多行匹配的
M
标志

实质上:

sed '/key1/{:a;N;/key5/!ba;/key3.*value32$/Md}' file
这可能适用于您(GNU-sed):

key1
key5
之间收集一组行,如果该组包含所需字符串,则删除整个组

注意:使用允许多行匹配的
M
标志

实质上:

sed '/key1/{:a;N;/key5/!ba;/key3.*value32$/Md}' file

我真的很喜欢第一个awk解决方案的简单性;您能解释一下为什么从正则表达式和ORS awk变量的格式中删除了“^”吗?另外,为什么我不能用${var}替换匹配模式中的value32,并用双引号替换简单引号?
^
表示“字符串的开始”(有时错误地称为“行的开始”)因为被处理的字符串通常是单行,就像人们常说的
$
表示“行结束”,但它不是,它表示“字符串结束”)。所讨论的字符串是一个多行的文本块,从<代码> -KE1:行开始,因此在代码中查找<代码> KE3:<代码>行是错误的,它在中间。使用
ORS=
我将
ORS
设置为空字符串,这样awk就不会在我的
print
语句后添加换行符,因为我已经将换行符打印作为
RT
的一部分。awk不是shell,它是一个完全独立的工具,具有自己的语法、语义和上下文<代码>${var}是在shell中获取shell变量值的方式-在从shell调用的awk脚本中,您无法做到这一点,就像在从shell调用的C程序中一样。除非需要双引号,否则应始终将shell脚本和字符串括在单引号中,请参阅以了解引号在shell中的工作方式,并参阅如何在awk脚本中使用shell变量的值。
awk-v val=“$var””。。。如果(rec!~(“\n[:blank:]{6}key3:[:blank:]“val”\n”)…
我非常喜欢第一个awk解决方案的简单性;您能解释一下为什么从正则表达式和ORS awk变量的格式中删除了“^”吗?另外,为什么我不能用${var}替换匹配模式中的value32,并用双引号替换简单引号?
^
表示“字符串的开始”(有时错误地称为“行的开始”)因为被处理的字符串通常是单行,就像人们常说的
$
表示“行结束”,但它不是,它表示“字符串结束”)。所讨论的字符串是一个多行的文本块,从<代码> -KE1:行开始,因此在代码中查找<代码> KE3:<代码>行是错误的,它在中间。使用
ORS=
我将
ORS
设置为空字符串,这样awk就不会在我的
print
语句后添加换行符,因为我已经将换行符打印作为
RT
的一部分。awk不是shell,它是一个完全独立的工具,具有自己的语法、语义和上下文<代码>${var}是在shell中获取shell变量值的方式-在从shell调用的awk脚本中,您无法做到这一点,就像在从shell调用的C程序中一样。您应该始终将shell脚本和字符串括在单引号中