2009-12-16 160 views
130

我有一个.csv文件是这样的:有没有一种方法可以按列“uniq”?

[email protected],2009-11-27 01:05:47.893000000,example.net,127.0.0.1 
[email protected],2009-11-27 00:58:29.793000000,example.net,255.255.255.0 
[email protected],2009-11-27 00:58:29.646465785,example.net,256.255.255.0 
... 

我必须从文件中删除重复的电子邮件(整行)(即在上面的例子中含有[email protected]线路之一)。如何仅在字段1上使用uniq(以逗号分隔)?根据manuniq没有列的选项。我试过sort | uniq,但它不起作用。

回答

229
sort -u -t, -k1,1 file 
  • -u独特
  • -t,所以逗号分隔符
  • -k1,1为重点领域1

测试结果:

[email protected],2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
+2

如果该列中包含逗号本身(带引号) – user775187 2011-06-17 10:18:56

+3

这是不行的唯一的事情是排序不会给你一个计数...我认为.. – Rodo 2014-01-14 11:00:51

+4

为什么你需要,1在-k1,1?为什么不只是-k1? – 2014-11-24 20:10:28

-2

好,简单不是孤立使用awk列,如果你需要某一个值,删除一切对于一个给定的文件,为什么不这样做的grep -v:

例如通过可能具有匹配COL1,COL2,COL3,COL4

grep -v ',col2,' file > file_minus_offending_lines 

如果这还不够好,因为有些线路可能会不恰当地剥夺:删除一切与排在第二位 行值“COL2”值显示在不同的列中,可以这样做:

awk隔离违规列: 例如

awk -F, '{print $2 "|" $line}' 

的-F设置分隔到外地“”,$ 2指塔2,其次是一些自定义分隔符,然后将整个线。

awk -F, '{print $2 "|" $line}' | grep -v ^BAD_VALUE 

,然后分隔符之前剥离出的东西:然后,您可以通过开始有错误的值删除线过滤

awk -F, '{print $2 "|" $line}' | grep -v ^BAD_VALUE | sed 's/.*|//g' 

(注意-The sed的命令是草率的,因为它不包括转义值,sed模式也应该是“[^ |] +”(即任何不是分隔符)的东西,但希望这已经足够清楚了

+2

他不想清除行,他想保留一个特定字符串行的单个副本。 Uniq是正确的用例。 – ingyhere 2015-11-13 01:34:38

-2

通过首先对sort进行排序,你可以申请uniq

这似乎文件就好了排序:

$ cat test.csv 
[email protected],2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
[email protected],2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 

$ sort test.csv 
[email protected],2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
[email protected],2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 

$ sort test.csv | uniq 
[email protected],2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
[email protected],2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 

你也可以做一些AWK魔术:

$ awk -F, '{ lines[$1] = $0 } END { for (l in lines) print lines[l] }' test.csv 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
[email protected],2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
[email protected],2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 
+0

根据问题中的要求,这不是唯一*列*。这对整条生产线来说是独一无二的。此外,你不必做一个uniq排序。这两者是相互排斥的。 – 2014-09-24 19:47:37

+1

是的,你是对的。尽管接受的答案是更清晰的,但最后一个例子确实解答了问题。 关于'sort',然后'uniq','sort'需要在执行'uniq'之前完成,否则它不起作用(但你可以跳过第二个命令并使用'sort -u')。 从['uniq(1)'](http://linux.die.net/man/1/uniq):“过滤来自INPUT(或标准输入)的相邻匹配行,写入OUTPUT(或标准输出) “。 – 2014-09-25 06:13:03

+0

啊,你在uniq之前排序是正确的。我从来没有意识到uniq只适用于相邻的线路。我想我总是使用sort -u。 – 2014-09-25 21:27:52

8

或者如果u想使用的uniq:

<mycvs.cvs tr -s ',' ' ' | awk '{print $3" "$2" "$1}' | uniq -c -f2

给出:

1 01:05:47.893000000 2009-11-27 [email protected] 
2 00:58:29.793000000 2009-11-27 [email protected] 
1 
+4

我想指出一个可能的简化:你可以转储'cat'!而不是管道到tr,只是让tr使用'<'读取文件。通过'cat'管道是新手使用的常见不必要的并发症。对于大量的数据,会有性能影响。 – 2009-12-16 16:27:22

+3

很高兴知道。谢谢! (当然这是有道理的,考虑“猫”和“懒惰”;)) – 2009-12-17 07:19:21

61
awk -F"," '!_[$1]++' file 
  • -F设置字段分隔符。
  • $1是第一个字段。
  • _[val]在散列_(常规变量)中查找val
  • ++增量,并返回旧值。
  • !返回逻辑不是。
  • 最后有一个隐式打印。
+1

这种方法比排序快两倍 – bitek 2015-02-17 21:12:02

+2

这还具有保持原始顺序的额外好处! – AffluentOwl 2015-03-10 00:21:57

+6

如果你需要* last * uniq而不是第一个,那么这个awk脚本将帮助:'awk -F',''{x [$ 1] = $ 0} END {for(i in x)print x [i]} '文件' – Sukima 2015-10-01 17:36:47

11

考虑多列。

排序,并给出了基于列1和列3独特的名单:

sort -u -t : -k 1,1 -k 3,3 test.txt 
  • -t :结肠是基于列1和列3
2

如果你想分离

  • -k 1,1 -k 3,3保留您可以使用的最后一个副本

    tac a.csv | sort -u -t, -r -k1,1 |tac 
    

    这是我的要求

    这里

    tac将线

  • 0

    反转文件中的行这里是一个非常巧妙的方法。

    首先格式化内容,使得唯一性比较的列是固定宽度。这样做的一种方法是使用awk printf与字段/列宽度说明符(“%15s”)。

    现在,uniq的-f和-w选项可用于跳过前面的字段/列和指定比较宽度(列宽)。

    这里有三个例子。

    在第一示例...

    1)暂时使感兴趣的固定宽度大于或等于该字段的最大宽度的列。

    2)使用-f uniq选项跳过以前的列,并使用-w uniq选项将宽度限制为tmp_fixed_width。

    3)从列中删除尾部空格以恢复它的宽度(假设事先没有尾部空格)。

    printf "%s" "$str" \ 
    | awk '{ tmp_fixed_width=15; uniq_col=8; w=tmp_fixed_width-length($uniq_col); for (i=0;i<w;i++) { $uniq_col=$uniq_col" "}; printf "%s\n", $0 }' \ 
    | uniq -f 7 -w 15 \ 
    | awk '{ uniq_col=8; gsub(/ */, "", $uniq_col); printf "%s\n", $0 }' 
    

    在第二个例子...

    创建一个新的uniq柱1。然后uniq的过滤器已被应用之后将其删除。

    printf "%s" "$str" \ 
    | awk '{ uniq_col_1=4; printf "%15s %s\n", uniq_col_1, $0 }' \ 
    | uniq -f 0 -w 15 \ 
    | awk '{ $1=""; gsub(/^ */, "", $0); printf "%s\n", $0 }' 
    

    第三个示例与第二个示例相同,但对于多个列。

    printf "%s" "$str" \ 
    | awk '{ uniq_col_1=4; uniq_col_2=8; printf "%5s %15s %s\n", uniq_col_1, uniq_col_2, $0 }' \ 
    | uniq -f 0 -w 5 \ 
    | uniq -f 1 -w 15 \ 
    | awk '{ $1=$2=""; gsub(/^ */, "", $0); printf "%s\n", $0 }' 
    
    相关问题