uniq 忽略一列的 csv 文件,也许是 awk?

jon*_*jon 7 awk text-processing sort csv

鉴于此文件(注释不是文件的一部分,而是构成解释的一部分)...

x,a,001,b,c,d,y
x,a,002,b,c,e,yy
x,bb,003,b,d,e,y
x,c,004,b,d,e,y
x,c,005,b,d,e,y   # nb - dupe of row 4
x,dd,006,b,d,e,y
x,c,007,b,d,e,y   # nb - dupe of row 4 and 5
x,dd,008,b,d,f,y
x,dd,009,b,d,e,y   # nb - dupe of row 6
x,e,010,b,d,f,y
Run Code Online (Sandbox Code Playgroud)

...我想得出以下输出:

x,a,001,b,c,d,y
x,a,002,b,c,e,yy
x,bb,003,b,d,e,y
x,c,004,b,d,e,y
x,dd,006,b,d,e,y
x,dd,008,b,d,f,y
x,e,010,b,d,f,y
Run Code Online (Sandbox Code Playgroud)

如果从文件中删除第 3 列,然后在文件上运行 uniq,那么如果剩余的行在正确的位置重新添加了第 3 列的值,那么我将得到上述结果。

但我真的很挣扎,想出一些可以做到这一点的东西。我很高兴有机会了解 linux 的文本处理实用程序。

性能:文件看起来不太可能增长到超过 1MB,而且每天只有 1 个文件。

目标:Debian GNU/Linux 7 amd64,256MB/至强。

编辑:调整示例,因为字段不是固定宽度,uniq --skip-chars=n据我所知,涉及的解决方案将不起作用。

Sté*_*las 18

使用awk,您可以执行以下操作:

awk -F, -vOFS=, '{l=$0; $3=""}; ! ($0 in seen) {print l; seen[$0]}'
Run Code Online (Sandbox Code Playgroud)

  • 哇,优雅而简单(而且可能也很快,使用哈希查找与前一行进行比较)。但是,它是否也删除了介于两者之间的重复项?(即,不同于OP要求的“uniq在文件上运行[如果删除了第3列]”?即:line1 =“x,a,001,b,c,d,y”,然后line12 =“x, a,999,b,c,d,y" 不会出现在您的解决方案中,但(也许)应该?) (2认同)
  • 你是对的,它在中间的东西之后删除了行,你是对的 uniq 不会这样做。但是如果你看 OP,他似乎相信 uniq 会按照这个脚本的方式行事,所以这个脚本可能是他真正想要的。 (2认同)

pet*_*rph 7

最简单的方法

sort -u -t, -k1,2 -k4
Run Code Online (Sandbox Code Playgroud)
  • -u: 只输出第一行等号
  • -t,: 使用逗号作为字段分隔符
  • -k1,2 -k4: 仅对字段 1,2 和 4 以及其余字段排序

另一种选择是在两侧重新排列数据sed(注意 GNU 选项-r) - 这要求记录大部分是固定长度的,否则它会失败(并且几乎不明显):

sort -u -t, -k1,2 -k4
Run Code Online (Sandbox Code Playgroud)

如果需要,您可能希望sort在最后添加另一个以按数字-k排序(使用该选项根据应执行的排序进行选择 - 即类似sed -k3 -t,

例如,在 Perl 中,您可以使用要确定唯一性的部分作为散列中的键(值整行),并仅在键尚未定义时插入到散列中。这当然比使用sed(or awk)灵活得多,而且写作也更多(我离 Perl Guru 还很远,所以很可能可以用更优雅的方式来完成 - 请参阅类似 Perl 的其他答案Perl 解决方案):

sed -r       's/^([^,]+,[^,]+)(,[^,]+)(.*)$/\1\3\2/' \
    | sort \
    | uniq -w 12 \
    | sed -r 's/^([^,]+,[^,]+)(.*)(,[^,]+)$/\1\3\2/'
Run Code Online (Sandbox Code Playgroud)