如果在两个模式之间找到文本,则使用 sed 删除两个模式之间的所有行

Using sed to remove all lines between two patters if text is found between them

提问人:user9082746 提问时间:10/4/2023 最后编辑:user9082746 更新时间:10/5/2023 访问量:103

问:

我有一个文件

###
A
B
C
@@@
###
D
E
F
@@@
###
G
H
I
@@@

如果它包含 A ,我想删除 ### 和 @@@(含)之间的所有行

我怎样才能做到这一点?

预期结果

###
D
E
F
@@@
###
G
H
I
@@@

我试过了

sed "/###/,/@@@/d"

但那是删除一切。

如果我们可以有多个文本模式,比如删除 ### 和 @@@ 之间的所有内容,如果它包含“A”或“D”,甚至会有所帮助。

谢谢。

Linux Bash 文本 SED

评论


答:

2赞 Wiktor Stribiżew 10/4/2023 #1

你可以使用

sed '/###/{:a;N;/@@@/!ba;/A/d}' file

细节

  • /###/- 寻找一个,一旦找到,###
  • :a- 在当前位置设置标签a
  • N- 读取下一行,将其附加到模式空间
  • /@@@/!ba- 如果有停止处理块,否则,转到标签@@@a
  • /A/d- 如果在匹配的块内,请将其删除。A

查看在线测试

#!/bin/bash
text=$(echo -e '###\nA\nB\nC\n@@@\n###\nD\nE\nF\n@@@\n###\nG\nH\nI\n@@@')
sed '/###/{:a;N;/@@@/!ba;/A/d}' <<< "$text"

输出:

###
D
E
F
@@@
###
G
H
I
@@@

评论

0赞 user9082746 10/4/2023
工作,谢谢。只是为了学习如何否定这个表达式,如果找到 A,请保留它,如果没有找到,请删除。
0赞 Wiktor Stribiżew 10/4/2023
@user9082746 它是使用 否定动作完成的:,或者我们可以只使用!sed '/###/{:a;N;/@@@/!ba;/A/!d}' fileAsed -n '/###/{:a;N;/@@@/!ba;/A/p}' file
2赞 RavinderSingh13 10/4/2023 #2

如果您同意,请尝试以下解决方案。在这里使用 GNU。awkawk

awk -v RS="" '
{
  while(match($0,/(#[^@]*)(@+)/,arr)){
    if(arr[1]!~/\nA\n/){
       print arr[1] arr[2]
    }
    $0=substr($0,RSTART+RLENGTH)
  }
}
' Input_file
0赞 Ed Morton 10/5/2023 #3

sed 非常适合在单个行上进行简单的替换,但如果您发现自己尝试使用比 s、g 和 p(带有 -n)命令更多的命令,那么几乎可以肯定最好使用 awk 来实现清晰度、效率、可移植性、健壮性等的某种组合。

使用 GNU awk for multi-char RS:

$ awk 'BEGIN{RS=ORS="\n@@@\n"} !/A/' file
###
D
E
F
@@@
###
G
H
I
@@@

$ awk 'BEGIN{RS=ORS="\n@@@\n"} /A/' file
###
A
B
C
@@@

$ awk 'BEGIN{RS=ORS="\n@@@\n"} /A|D/' file
###
A
B
C
@@@
###
D
E
F
@@@

或带有任何 awk:

$ awk '{r=r $0 ORS} /^@@@$/{ if (r !~ /A/) printf "%s", r; r="" }' file
###
D
E
F
@@@
###
G
H
I
@@@

$ awk '{r=r $0 ORS} /^@@@$/{ if (r ~ /A/) printf "%s", r; r="" }' file
###
A
B
C
@@@

$ awk '{r=r $0 ORS} /^@@@$/{ if (r ~ /A|D/) printf "%s", r; r="" }' file
###
A
B
C
@@@
###
D
E
F
@@@

当 A 和 D 是如上所述的单字符时,您当然可以在正则表达式中使用括号表达式的一组字符,而不是正则表达式中的交替,但我假设您的实际数据并不总是单个字符。[AD]A|D

请注意,将这种比较扩展到任何一组“ands”、“ors”甚至 、 ,或者将行与当前记录或以前记录中的其他值进行比较是多么微不足道,例如:<>

$ awk 'BEGIN{RS=ORS="\n@@@\n"} /A/ && (/B/ || /C/) && !/D/' file
###
A
B
C
@@@

无需测试,因为它始终存在于每条记录的开头,只需测试终止每条记录的记录即可。###@@@

当出现在输入的其他地方而不是作为记录终止符时,周围的 s 或 和 锚点是必要的,以避免错误匹配,例如,给定以下输入:\n^$@@@@@@

$ cat file
###
A
this is where @@@ is undesirable
C
@@@
###
D
E
F
@@@
###
G
H
I
@@@

上述命令仍然有效:

$ awk 'BEGIN{RS=ORS="\n@@@\n"} /A/' file
###
A
this is where @@@ is undesirable
C
@@@

但是,如果我们删除周围的锚点,它们将产生错误的匹配:@@@

$ awk 'BEGIN{RS=ORS="@@@"} /A/' file
###
A
this is where @@@$