删除第一列重复的行

删除第一列重复的行

我有一个文件,其中字符串由特殊字符串(不是逗号或分隔符)分隔,例如<vvv>.我想检查第一个字段中的所有字符串是否都是唯一的。如果发现同一字段的重复行,我想删除任何重复的行(保留第一个出现的行)。

例子:

aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz
aaa<vvv>new<vvv>new2
111<vvv>222<vvv>333

我想得到:

aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz
111<vvv>222<vvv>333

我们删除了,aaa<vvv>new<vvv>new2因为aaa已经出现了。

我不喜欢我们,awk除非这是唯一的解决方案。对于不熟悉 Linux 的我来说,它的语法有点复杂。

答案1

不使用awk 非常:

$ awk -v OFS="<" '{ print NR, $0 }' file | sort -t '<' -u -k2,2 | sort -t '<' -k1,1n | cut -d '<' -f 2-
aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz
111<vvv>222<vvv>333

这只用于在原始数据中awk插入行号。<我们这样做是为了能够跟踪原始行的顺序。我们使用<作为行号和行的其余部分之间的分隔符,因为它也显示为原始第一个字段和行的其余部分之间的分隔符。

在管道的第一阶段用于awk插入行号之后,数据将如下所示

1<aaa<vvv>bbb<vvv>ccc
2<xxx<vvv>yyy<vvv>zzz
3<aaa<vvv>new<vvv>new2
4<111<vvv>222<vvv>333

管道的下一步将在第二个字段(第一个原始字段)上对此进行排序,删除重复项。结果将是

4<111<vvv>222<vvv>333
1<aaa<vvv>bbb<vvv>ccc
2<xxx<vvv>yyy<vvv>zzz

第二个sort通过对第一个字段上的行进行数字排序来恢复原始行顺序,我们得到

1<aaa<vvv>bbb<vvv>ccc
2<xxx<vvv>yyy<vvv>zzz
4<111<vvv>222<vvv>333

然后cut从第一个字段(以及插入的分隔符)中删除数字。


无需使用即可提供有序输出的解决方案awk如下所示

$ sort -t '<' -u -k1,1 file
111<vvv>222<vvv>333
aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz

这本质上是上述管道中的第二步,它对第一个字段上的文件进行排序,同时删除重复项。


一个awk解决方案看起来像

$ awk -F '<' '!seen[$1]++' file
aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz
111<vvv>222<vvv>333

这会将第一个字段存储为名为 的关联数组中的键,seen并后递增关联值。如果给定键的数组中的值为零(即,之前没有见过第一个字段),则打印该行。

答案2

或者等效地既不使用awk也不使用cut,但使用sed

$ sed '=' file \
      | sed 'N;s/\n/</' \
      | sort -t"<" -u -k2,2 \
      | sort -t"<" -k1,1 \
      | sed 's/^[0-9]*<//'
aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz
111<vvv>222<vvv>333

但这是非常笨重的。 @Kusalananda 的最后一个(基于 awk)解决方案是很多更好的。


仅出于教学目的,sed上面的前两个块相当于 Kusalananda 的更紧凑的awkcmd :

  • sed '=' file,打印行号以供将来订购
  • sed 'N;s/\n/</',在模式空间中追加下一个输入行(即“连接当前行和下一行”)并将行尾替换\n<

第三个也是最后一个sed花絮,sed 's/^[0-9]*<//', 替换了之前放在每行开头的行号和“<”,什么也没有。


有关 的更多详细信息sed,请$ info sed在控制台中发出问题。

答案3

使用 GNU sed 我们可以完成给定的任务:

$ sed -Ene '
   G
   /^([^<]+)<vvv>.*\n\1(\n|$)/d
   P;s/<vvv>.*//;H
 ' input.txt

将第一个字段存储在保留空间中并将其与当前行的第一个字段进行比较。仅当它们不同时,才更新保留并打印当前行。

答案4

尝试过以下2种方法

Method1

 awk -F "<" '{if (!seen[$1]++)print }' filename

Method2

awk -F "<" '!a[$1]++' filename

输出

aaa<vvv>bbb<vvv>ccc
xxx<vvv>yyy<vvv>zzz
111<vvv>222<vvv>333

相关内容