删除 CSV 中一个字段中具有重复值的连续行,但保留最后一行

删除 CSV 中一个字段中具有重复值的连续行,但保留最后一行

我有一个很长的 CSV 文件,有两列,其中包括连续的重复项,如下所示:

...
1500,1533
1554,1678
1554,1703
1554,1728
1593,1766
...

我需要删除除最后一个之外的所有重复项 - 因此上面示例的输出将是:

...
1500,1533
1554,1728
1593,1766
...

另外,我需要将文件中的其余行保留为原始顺序。

我试过tac file.csv | sort -k1,1 -r -u -t,

但这并没有给出预期的结果,并且基于排序的函数弄乱了我的行顺序。

答案1

sed

sed '$!N;/\(.*,\).*\n\1/!P;D' infile

N意味着模式空间中总是有两行连续的行,并且sed P仅当该行中的第一个字段与第二行中的第一个字段不同时才打印其中的第一行。然后D从模式空间中删除第一行并重新开始循环。


另一种方式是gnu datamash(假设您的文件按照datamash需要排序的输入进行排序):

datamash -t ',' -g 1 last 2 <infile

这将按 st 字段对分隔输入g进行分组,仅打印每组的值(来自第 nd 列)。,1last2


如果您的文件未排序,datamash可以通过以下方式对其进行排序-s

datamash -t ',' -s -g 1 last 2 <infile

但这意味着行的初始顺序将不会被保留。所以这可能不会达到你想要的效果。在这种情况下,你可以使用sed/ awk/perl等...

答案2

还有一个替代的 awk:

 awk -F, 'NR==1{old=$0;check=$1}NR>1 && $1 != check {print old}{old=$0;check=$1}END{print old}' knovice
1500,1533
1554,1728
1593,1766

答案3

这是另一种awk方法(谢谢@格伦):

 tac file | awk -F, 'awk -F, '!seen[$1]++' | tac

设置-F,分隔符。在 中awk,当表达式计算结果为 true 时,默认操作是打印当前行。!seen[$1]当数组中不存在第一个字段时将为 true seen。然而,由于我们也是用 创建它的seen[$1]++,所以只有在第一次看到它时才会是错误的。结果是仅打印第一个副本。

由于上面的脚本将保留每次重复运行的第一个而不是最后一个,因此这两个tac调用是一个丑陋的黑客,可以颠倒顺序并使其保留最后一个。由于有两个,所以最终顺序不会改变。

答案4

使用 Miller ( mlr) 获取每组中的最后一个,同时按第一个字段进行分组:

$ mlr --csv -N tail -n 1 -g 1 file
1500,1533
1554,1728
1593,1766

请注意,您只需在上面的命令中更改tail -n 1为即可获得最后两个。tail -n 2

相关内容