2009-12-16 162 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 }' 
    
    相關問題