我有一个很长的 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 列)。,
1
last
2
如果您的文件未排序,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