关于unix:根据一个匹配的字段去掉第一个重复的行,保留第二个匹配的行

remove the first duplicate line based on a matched field and keep the second matched line

  • 输入文件有 3 个字段。每个字段由 | 分隔(管道)。
  • 第一个字段是关键字段并已排序。第一个字段中的每个键可能出现一次或两次。
  • 如果第一个字段中存在两次相同的键,则删除
    第一次出现,不要删除第二次出现的行。
  • 如果一个键只出现一次,则不要删除该行。
  • 第三个字段中的输入数据在整个文件中都是唯一的。
  • 尝试了以下命令,该命令保留了第一个重复行并删除了其余的重复行。 awk 命令中是否有任何选项可以删除第一个匹配的重复行并保留第二个匹配的行。 awk 以外的命令也可以。输入文件大小可以是 50 GB。我现在正在测试 12 GB 文件。

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

    输入文件内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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 提供的以下解决方案


    有效地使用 awk 表达式:

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

    "魔术"基于捕获每个当前记录(有效地覆盖它而无需不断累积)并在下一行执行分析。

    样本输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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"}

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

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

    第二个解决方案:如果您担心订单,请使用 awk 更少的数组和 sort 再添加 1 个解决方案。

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

    第三种解决方案:请您尝试以下方法。如果您担心输出的顺序应该与所示 Input_file.

    相同

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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
    2
    3
    4
    5
    6
    7
    8
    9
    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"}


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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
    2
    3
    4
    5
    6
    7
    8
    9
    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 以便它可以从后面读取文件并使用带有 sort-T, --temporary-directory=DIR 选项来允许它对如此大的文件进行排序(或者不排序,如果你有足够的内存) .


    此单行只会从您的文件中删除第一个重复项(第二次出现)。

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

    我们来看一个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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