224

我有一个这样的 .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上面示例中包含的行之一)。如何uniq仅在字段 1 上使用(以逗号分隔)?根据man,uniq没有列选项。

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

4

8 回答 8

377
sort -u -t, -k1,1 file
  • -u为独一无二
  • -t,所以逗号是分隔符
  • -k1,1对于关键字段 1

测试结果:

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 
于 2009-12-16T16:14:50.927 回答
122
awk -F"," '!_[$1]++' file
  • -F设置字段分隔符。
  • $1是第一个字段。
  • _[val]val在散列_(一个常规变量)中查找。
  • ++递增,并返回旧值。
  • !返回逻辑非。
  • 最后有一个隐式打印。
于 2009-12-16T17:17:05.757 回答
19

考虑多列。

根据第 1 列和第 3 列排序并给出唯一列表:

sort -u -t : -k 1,1 -k 3,3 test.txt
  • -t :冒号是分隔符
  • -k 1,1 -k 3,3基于第 1 列和第 3 列
于 2014-01-21T01:30:06.550 回答
8

或者如果你想使用 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
于 2009-12-16T16:18:03.853 回答
7

如果您想保留可以使用的最后一个重复项

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

这是我的要求

这里

tac将逐行反转文件

于 2015-12-14T13:46:26.720 回答
1

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

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

现在 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 }'
于 2017-04-25T01:22:31.513 回答
-3

好吧,比使用 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 模式实际上应该类似于“[^|]+”(即不是分隔符的任何内容)。但希望这足够清楚。

于 2009-12-16T16:13:10.023 回答
-3

通过sort首先对文件进行排序,然后您可以应用uniq.

似乎对文件进行了很好的排序:

$ 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 
于 2009-12-16T16:14:37.647 回答