有没有一种方法来“列”uniq?

我有一个.csv文件,像这样:

stack2@example.com,2009-11-27 01:05:47.893000000,example.net,127.0.0.1 overflow@example.com,2009-11-27 00:58:29.793000000,example.net,255.255.255.0 overflow@example.com,2009-11-27 00:58:29.646465785,example.net,256.255.255.0 ... 

我必须从文件中删除重复的电子邮件(整个行)(即上面例子中包含overflow@example.com的行之一)。 如何仅在字段1上使用uniq (以逗号分隔)? 据说, uniq没有列的选项。

我尝试了一些东西sort | uniq sort | uniq但它不工作。

 sort -u -t, -k1,1 file 
  • -u为唯一
  • -t,所以逗号是分隔符
  • -k1,1为关键字段1

testing结果:

 overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0 stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 
 awk -F"," '!_[$1]++' file 
  • -F设置字段分隔符。
  • $1是第一个字段。
  • _[val]在hash _ (一个常规variables)中查找val
  • ++增量,并返回旧值。
  • ! 返回逻辑不是。
  • 最后有一个隐含的印刷品。

考虑多个栏目。

根据第1列和第3列进行sorting并给出唯一列表:

 sort -u -t : -k 1,1 -k 3,3 test.txt 
  • -t :冒号是分隔符
  • -k 1,1 -k 3,3基于列1和列3

或者如果你想使用uniq:

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

得到:

 1 01:05:47.893000000 2009-11-27 tack2@domain.com 2 00:58:29.793000000 2009-11-27 overflow@domain2.com 1 

如果你想保留你可以使用的最后一个副本

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

这是我的要求

这里

tac将逐行反转文件

这是一个非常漂亮的方式。

首先格式化内容,以便比较唯一性的列是固定宽度。 这样做的一种方法是使用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 }' 

在第二个例子中

创build一个新的uniq列1.然后在应用uniqfilter后将其删除。

 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 }' 

好了,比用awk隔离列更简单,如果你需要删除给定文件的某个值,那为什么不直接执行grep -v:

例如删除第二行中的值“col2”:col1,col2,col3,col4

 grep -v ',col2,' file > file_minus_offending_lines 

如果这还不够好,因为某些行可能会被不正确地剥离,可能会将匹配的值显示在不同的列中,您可以这样做:

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' 

(注意:sed命令是不严格的,因为它不包含转义值,sed模式也应该是“[^ |] +”(即任何不是分隔符)的东西,但是希望这个清楚。

通过首先sorting文件,然后可以应用uniq

它似乎sorting文件就好了:

 $ cat test.csv overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0 stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 $ sort test.csv overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0 stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 $ sort test.csv | uniq overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0 overflow@domain2.com,2009-11-27 00:58:29.793000000,xx3.net,255.255.255.0 stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 stack4@domain.com,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 stack2@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 stack4@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 stack3@domain.com,2009-11-27 01:05:47.893000000,xx2.net,127.0.0.1 overflow@domain2.com,2009-11-27 00:58:29.646465785,2x3.net,256.255.255.0