如何删除以相同单词开头的连续两行中的第二行?

14

我有一个文本文件,其中的行会交替以“TITLE”和“DATA”开头,但有时会有以“TITLE”开头的重复行:

TITLE something
DATA some data
TITLE something else
DATA some other data
TITLE some more
TITLE extra info
DATA some more data

我想能够检测到以“TITLE”开头的重复行,并仅保留每对这样的行中的第一行。
我发现捕获它们的正则表达式是 ^TITLE.*\n^TITLE.*\n,现在我想将其合并到一个命令中,使用perl/bash/sed/awk 命令删除第二行,并输出文件的其余部分,但我无法解决这个问题。


2
奖励问题:如何仅保留最后一个这样的连续行? - Nikana Reklawyks
1
最短的解决方案:uniq -w5 your_file - Nikana Reklawyks
5个回答

8
以下是一种使用 GNU sed 工具的方法:
```sed```
sed -r 'N; /(TITLE)[^\n]*\n\1/ s/\n.*//; P; D' infile
  • N 在模式空间中添加第二行。
  • 匹配测试是否两行都以TITLE开头。
  • 如果是,则删除第二行。
  • P; D 打印并删除模式空间中的第一行。

输出:

TITLE something
DATA some data
TITLE something else
DATA some other data
TITLE some more
DATA some more data

编辑 - 处理任意数量的重复

正如评论中由Nikina Reklawyks指出的那样,上述解决方案仅适用于以TITLE开头的两行连续内容,为处理任意数量的重复,可以添加简单循环,如下所示:

sed -r ':a; N; /(TITLE)[^\n]*\n\1/ s/\n.*//; ta; P; D' infile
< p > ta语句使sed在s///成功时跳转到:a标签。

另一种方法是使用coreutils中的uniq命令,虽然不够灵活,但在这种情况下仍能很好地工作:

uniq -w5 infile 

1
这只会删除连续出现的第二行,而保留第一行则是自然而又理想的扩展(我这么认为)。换句话说,如何将其包装成一个“运行直到输出不再改变”的循环?无论如何,感谢提供如此有用的起点。 - Nikana Reklawyks
@NikanaReklawyks:没错,在这种情况下添加循环很容易,参见编辑。 - Thor

7

Perl解决方案:

perl -ne 'print unless $t and /^TITLE/; $t = /^TITLE/'

它会记住前一行是否为$t变量中的标题。

4

一种方式:

awk '$1!=p{print;p=$1}' file

3

听起来你有一些由两个字段“标题”和“数据”组成的记录,如果第二个字段缺失,你想要删除这条记录。不过这不是你在问题中问到的。以下是一种满足你要求的方法:

awk '/^TITLE/&&!t{t=$0} /^DATA/&&t{print t;print;t=""}' inputfile

这里的想法是,当我们看到一个TITLE并且还没有设置标题时,我们会将一个变量设置为该标题,并且只在看到DATA时打印它。如果我正确理解了你的问题,这对你提供的输入数据是有效的。输出结果为:

TITLE something
DATA some data
TITLE something else
DATA some other data
TITLE some more
DATA some more data

正如您所看到的,数据集中的最后一行标题被删除。

以下是awk中另一种执行此操作的方法...

awk '/^TITLE/&&t{next} t=0; /^TITLE/{t=1} 1' inputfile

在这个例子中,第一个表达式会跳过标题(如果已经设置了t)。第二个表达式取消设置t。第三个表达式为标题设置标志,最后一个表达式(1)输出该行内容。当然,如果我们在第一个表达式中跳过了该行,则不会运行最后三个表达式。这个例子生成与上面相同的输出,并且不会查看/^DATA/

最后,这个例子代码最短,但逻辑最古怪:

awk '/^DATA/ || !t; {t=/^TITLE/}' inputfile

它会打印所有数据行,或任何未设置t的行,然后有效地将t设置为布尔值,影响下一行的计算。如果您在csh或tcsh中执行此操作,请注意感叹号,在这些shell中可能需要进行转义。

2

试试这个一行代码:

 awk '/^TITLE/&&f{next;} {if ($0~/^TITLE/)f=1;else f=0}1' file

输出:

TITLE something
DATA some data
TITLE something else
DATA some other data
TITLE some more
DATA some more data

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接