14

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

TITLE 一些
DATA 一些数据
TITLE 一些其他
DATA 一些其他数据
TITLE 一些更多
TITLE 额外信息
DATA 一些更多数据

我希望能够检测到以 'TITLE' 开头的重复行,并只保留每对这样的第一行。
我发现捕获这些的正则表达式^TITLE.*\n^TITLE.*\n现在我想将其合并到一个单行 ///perl命令bashsedawk该命令将删除第二行并输出文件的其余部分,但我无法弄清楚.

4

5 回答 5

7

Perl 解决方案:

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

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

于 2013-03-15T11:14:53.013 回答
7

这是使用 GNU 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

ta语句在成功时使 sed 跳转到:a标签s///

另一种方法是使用uniqfrom 的命令coreutils,这不是很灵活,但在这种情况下效果很好:

uniq -w5 infile 
于 2013-03-15T11:31:02.020 回答
4

单程:

awk '$1!=p{print;p=$1}' file
于 2013-03-15T11:12:51.547 回答
3

在我看来,您的记录包含两个字段,即 TITLE 和 DATA,如果您缺少第二个字段,您想删除该记录。但这不是你在问题中问的。因此,这是执行您要求的一种方法:

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

如您所见,数据集中的最后一个 TITLE 行已被删除。

这是在 awk 中执行此操作的另一种方法...

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

t在这一个中,如果已设置,第一个表达式会跳过标题。第二个表达式 unsets t。第三个表达式为标题设置 if,最后一个表达式 ( 1) 打印该行。当然,如果我们跳过第一个表达式中的行,最后三个表达式就不会运行。它生成与上面相同的输出,并且不费心查看/^DATA/.

最后,这是代码最少但逻辑最奇怪的一个:

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

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

于 2013-03-15T11:36:55.853 回答
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
于 2013-03-15T11:12:34.783 回答