【问题标题】:remove the first duplicate line based on a matched field and keep the second matched line根据匹配字段删除第一个重复行并保留第二个匹配行
【发布时间】:2020-03-28 02:20:53
【问题描述】:
  1. 输入文件有 3 个字段。每个字段由 | (PIPE) 分隔。
  2. 第一个字段是关键字段并已排序。第一个字段中的每个键可能出现一次或两次。
  3. 如果相同的键在第一个字段中存在两次,则删除 第一次出现,不要删除第二次出现的行。
  4. 如果一个键只出现一次,则不要删除该行。
  5. 第三个字段中的输入数据在整个文件中都是唯一的。

尝试了以下命令,该命令保留第一个重复行并删除其余重复行。 awk 命令中是否有任何选项可以删除第一个匹配的重复行并保留第二个匹配的行。 awk 以外的命令也可以。输入文件大小可以是 50 GB。我现在正在测试 12 GB 的文件。

awk -F'|' '!a[$1]++'

输入文件内容:

1|xxx|{name: "xyz"}
2|xxx|{name: "abcfgs"}
3|xxx|{name: "egg"}
4|xxx|{name: "eggrgg"}
5|xxx|{name: "xsdsyzsgngn"}
5|xxx|{name: "gbgnfxyz"}
6|xxx|{name: "xyz"}
7|xxx|{name: "xynfnfnnnz"}
7|xxx|{name: "bvbv"}
8|xxx|{name: "xyz"}
9|xxx|{name: "xyz"}
....

处理输入文件后的预期输出:

1|xxx|{name: "xyz"}
2|xxx|{name: "abcfgs"}
3|xxx|{name: "egg"}
4|xxx|{name: "eggrgg"}
5|xxx|{name: "gbgnfxyz"}
6|xxx|{name: "xyz"}
7|xxx|{name: "bvbv"}
8|xxx|{name: "xyz"}
9|xxx|{name: "xyz"}
....

编辑

分别尝试了 @RavinderSingh13@RomanPerekhrest 提供的以下解决方案。

对于 12GB 的输入文件,以下解决方案在第一次运行时耗时 1 分 20 秒,在第二次运行时耗时 1 分 46 秒:

awk '
BEGIN{
  FS="|"
}
!a[$1]++{
  b[++count]=$1
}
{
  c[$1]=$0
}
END{
  for(i=1;i<=count;i++){
    print c[b[i]]
  }
}
' Inputfile  > testawk.txt

对于 12GB 的输入文件,以下解决方案第一次运行耗时 2 分 31 秒,第二次运行耗时 4 分 43 秒,第三次运行耗时 2 分钟:

awk -F'|' 'prev && $1 != prev{ print row }{ prev=$1; row=$0 }END{ print row }' Inputfile > testawk2.txt

两种解决方案都按预期工作。在进行几次性能测试后,我将使用上述任何一种。

【问题讨论】:

  • 我没有从预期的输出中看到任何重复的删除。请详细说明。
  • @JamesBrown 做到了。关键是$1
  • 第三个、第四个……应该像第一个一样被删除吗? NO重复的情况呢,还需要看一审吗?
  • 在你的例子中,是前缀 '5|xxx|'只是意味着输入文件 xxx 的第 5 行?或者它是输入的一部分?
  • 为什么预期的输出包含第 1 行中的“xyz” - 是否应该因为第 6 行中的“xyz”而将其删除?

标签: unix awk duplicates match


【解决方案1】:

有效地使用 awk 表达式:

awk -F'|' 'prev && $1 != prev{ print row }{ prev=$1; row=$0 }END{ print row }' file

“魔法” 是基于捕获每条当前记录(有效地覆盖它而无需不断积累)并对 下一个 行执行分析。

样本输出:

1|xxx|{name: "xyz"}
2|xxx|{name: "abcfgs"}
3|xxx|{name: "egg"}
4|xxx|{name: "eggrgg"}
5|xxx|{name: "gbgnfxyz"}
6|xxx|{name: "xyz"}
7|xxx|{name: "bvbv"}
8|xxx|{name: "xyz"}
9|xxx|{name: "xyz"}

【讨论】:

    【解决方案2】:

    第一个解决方案:如果您完全不担心输出中的行顺序,那么就简单地做。

    awk 'BEGIN{FS="|"} {a[$1]=$0} END{for(i in a){print a[i]}}' Input_file
    


    第二个解决方案:再添加 1 个解决方案,减少数组 awksort,以防您担心订单。

    awk 'BEGIN{FS="|"} {a[$1]=$0} END{for(i in a){print a[i]}}' Input_file | sort -t'|' -k1
    


    第三种解决方案:您能否尝试以下操作。如果您担心输出的顺序应该与显示的 Input_file 相同。

    awk '
    BEGIN{
      FS="|"
    }
    !a[$1]++{
      b[++count]=$1
    }
    {
      c[$1]=$0
    }
    END{
      for(i=1;i<=count;i++){
        print c[b[i]]
      }
    }
    '  Input_file
    

    输出如下。

    1|xxx|{name: "xyz"}
    2|xxx|{name: "abcfgs"}
    3|xxx|{name: "egg"}
    4|xxx|{name: "eggrgg"}
    5|xxx|{name: "gbgnfxyz"}
    6|xxx|{name: "xyz"}
    7|xxx|{name: "bvbv"}
    8|xxx|{name: "xyz"}
    9|xxx|{name: "xyz"}
    

    【讨论】:

    • 请记住,OP 说文件是 50G。 c[] 存储整个文件,如果我理解代码正确的话。
    • @Kent,是的,我也提出了 2 次 Input_file 阅读概念,但不确定这是否会比这更快。
    • @Kent,您好,先生,我的第二个解决方案怎么样?恕我直言,我相信这应该比我以前的更快,你说的先生。
    • 这与更快或更慢无关。不知道如果OP的系统有
    【解决方案3】:

    此单行将仅删除文件中的第一个重复项(第二次出现)

    awk 'a[$1]++ !=1' file
    

    我们来看一个例子:

    kent$  cat f
    1
    2
    3
    2 <- should be removed
    4
    3 <- should be removed
    5
    6
    7
    8
    9
    2 <- should be kept
    3 <- should be kept
    10
    
    kent$  awk 'a[$1]++ !=1' f
    1
    2
    3
    4
    5
    6
    7
    8
    9
    2
    3
    10
    

    【讨论】:

    • 对我来说,第一次出现应该被删除,第二次出现应该存在。
    • @inzero 所以你只想拥有重复的数据?例如。 5 在我的例子中,没有重复,应该保留还是不保留?请制作一个更好的输入/输出示例。我完全糊涂了。
    • @inzero 如果您有 10 行具有相同的键,您想在输出中包含第 2-10 行吗?
    • 只有 1 或 2 行具有相同的键。
    【解决方案4】:

    反转文件并稳定唯一排序:

    cat <<EOF |
    1|xxx|{name: "xyz"}
    2|xxx|{name: "abcfgs"}
    3|xxx|{name: "egg"}
    4|xxx|{name: "eggrgg"}
    5|xxx|{name: "xsdsyzsgngn"}
    5|xxx|{name: "gbgnfxyz"}
    6|xxx|{name: "xyz"}
    7|xxx|{name: "xynfnfnnnz"}
    7|xxx|{name: "bvbv"}
    8|xxx|{name: "xyz"}
    9|xxx|{name: "xyz"}
    EOF
    tac | sort -s -t'|' -k1,1 -u
    

    会输出:

    1|xxx|{name: "xyz"}
    2|xxx|{name: "abcfgs"}
    3|xxx|{name: "egg"}
    4|xxx|{name: "eggrgg"}
    5|xxx|{name: "gbgnfxyz"}
    6|xxx|{name: "xyz"}
    7|xxx|{name: "bvbv"}
    8|xxx|{name: "xyz"}
    9|xxx|{name: "xyz"}
    

    tac 是一个 GNU 实用程序。因为您的文件很大,请将文件名传递给tac,以便它可以从后面读取文件并使用-T, --temporary-directory=DIR 选项和sort 来允许它对如此大的文件进行排序(或者不排序,如果你有足够的内存) .

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-25
      • 1970-01-01
      • 2016-10-28
      • 1970-01-01
      • 1970-01-01
      • 2016-03-06
      • 2012-04-30
      相关资源
      最近更新 更多