好吧,我想删除重复的行,但它比这更复杂一点..
我有一个名为 users.txt 的文件,文件示例是:
users:[email protected]
users1:[email protected]
现在,由于我的系统中的一个错误,人们可以使用与其他人相同的电子邮件地址进行注册,因此我想删除如果行多次具有相同电子邮件地址的情况,问题示例:
user:display:[email protected]
user2:[email protected]
user3:[email protected]
user4:[email protected]
请注意 user、user2、user3、user4 是如何拥有相同的电子邮件的。我想删除 user2、user3、user4 但保留 user.. 反之亦然(按请求选取第一个)删除包含相同电子邮件的任何其他行电子邮件..
因此,如果
[email protected] is in 20 lines remove 19
[email protected] is in 555 lines remove 554
and so fourth..
答案1
这需要 awk。由于您要检查的字段是每行的第一个字段,因此只需引用$1
.
awk -F: '! ($1 in seen) {print; seen[$1]}' users.txt
您可以“打高尔夫球”以大大减少它:
awk -F: '!a[$1]++' users.txt
较长的形式或多或少是不言自明的;您使用每个电子邮件地址作为索引构建一个关联数组,而无需费心分配值。然后,您可以检查电子邮件地址之前是否已“见过”(即,关联数组是否已将特定电子邮件地址作为索引),如果没有,则打印整行。
较短的形式实际上或多或少做了相同的事情,但需要对较短的代码进行更多解释。
后缀++
运算符作用于变量后表达式会被求值,所以我们稍后会再讨论这个问题。
在 awk 中,0 表示 false,非零表示 true。 !
是为了否定并反转真值。
出现在大括号之外时,该表达式被解释为布尔表达式,如果表达式为 true,则执行关联的操作(在大括号中)。由于没有明确说明任何操作,因此使用打印整行的默认(隐式)操作,如果表达式的计算结果为真(非零)。
a
本质上,这会检索关联数组中指向电子邮件地址(第一个字段)的值作为其索引,或者创建初始化为 0 的值(如果尚不存在),将 0 解释为 false 或将非零解释为 true,反转如果结果是“真值”,则打印整行,然后递增该点存储在关联数组中的值。
实际上,这是一个足够常见的 Awk 习惯用法,但我不会责怪您使用更长、更明确的版本。 :)
答案2
使用GNU
datamash
将输入分组为第二名字段,并仅保留每个分组的第一行:datamash -t':' -g 2 rmdup 2 < users.txt
作为来自的评论唐克里斯斯蒂注释,
sort
可以做到这一点,但是当它返回所需的结果时,它也可能会重新排序输出:sort -t':' -k 2,2 -u users.txt
上面的代码假设用户.txt按第二个字段排序,然后按第一个字段排序。