如何根据sed中前一行和当前行中的匹配删除当前行?

时间:2012-10-01 18:47:57

标签: unix sed

给定一个如此排序的文件:

AAA 1 2 3
AAA 2 3 4
AAA 3 4 2
BBB 1 1 1
BBB 1 2 1

的期望输出
AAA 1 2 3
BBB 1 1 1

使用sed实现这一目标的最佳方法是什么?

基本上,如果col以与上一行相同的字段开头,我该如何删除它?其余数据必须保留在输出中。

我想必须有一些方法可以使用保持缓冲区,分支或测试命令来执行此操作。

6 个答案:

答案 0 :(得分:1)

这可以用AWK完成:

$ gawk '{if (last != $1) print; last = $1}' in.txt
AAA 1 2 3
BBB 1 1 1

答案 1 :(得分:1)

使用awk的另一种方式:

awk '!($1 in a){print;a[$1]}' file

答案 2 :(得分:0)

也许sed有一种更简单的方式,但是:

sed ':a;N;/\([[:alnum:]]*[[:space:]]\).*\n\1/{s/\n.*//;ta};P;D'

这会产生输出

AAA 1 2 3
BBB 1 1 1

与问题中的不同,但与描述匹配:

  

如果col以与上一行相同的字段开头,我该如何删除它?

答案 3 :(得分:0)

这可能适合你(GNU sed):

sed -r ':a;$!N;s/^((\S+\s).*)\n\2.*/\1/;ta;P;D' file

或者只是:

sort -uk1,1 file

答案 4 :(得分:0)

使用GNU awk的一种方式:

awk '!array[$1]++' file.txt

结果:

AAA 1 2 3
BBB 1 1 1

答案 5 :(得分:0)

使用sed:

#!/bin/sed -nf

P

: loop
s/\s.*//
N
/\([^\n][^\n]*\)\n\1/ b loop

D

首先,我们必须将-n标记传递给sed,这样它才能打印我们告诉它的内容。

我们首先使用“P”命令打印该行,因为第一行将始终打印,我们将强制sed仅在我们需要时执行此行。

现在我们要做一个循环。我们通过“:”命令定义一个带有起始标签的循环(在这种情况下,我们将标签命名为“loop”),并在必要时使用“b”命令(或“t”测试)跳回到此标签命令)。这个循环非常简单:

  1. 删除除第一个字段之外的所有内容(替换第一个空格字符及其后面的所有字符)
  2. 附加下一行(将包含换行符)
  3. 检查新行是否以我们隔离的字段开头。我们通过使用捕获来完成此操作。捕获被定义为“子匹配”,其匹配的输入将被存储到特殊的“变量”中,按照存在的捕获顺序以数字命名。我们使用带有反向转义的括号进行指定捕获(以\(开头,以\)结尾)。在这种情况下,我们匹配所有不是换行符的字符(即。[^\n])直到行的末尾。我们通过匹配至少一个非换行符后跟任意序列来完成此操作。这可以防止在换行符之前匹配空字符串。捕获后,我们使用特殊变量\1匹配换行符后跟捕获结果,其中包含与第一次捕获匹配的输入。如果成功,我们有一行重复第一个字段,所以我们用“b”分支命令跳回到循环的开头。
  4. 当我们退出循环时,我们找到了一条具有不同第一个字段的行,因此我们必须准备输入行并跳回到脚本的开头。这可以使用“D”delete-first-line-and-restart-script命令完成。
  5. 这可以缩短为一行(注意我们已将“loop”标签重命名为“a”):

    sed -e 'P;:a;s/\s.*//;N;/\([^\n][^\n]*\)\n\1/ba;D'