显示在字符串子集中包含重复项的行

Display lines containing duplicates within subset of string

如何通过只匹配每行的一部分而不是整行本身来找到重复行?

以下面的文字为例:

uid=154163(j154163) gid=10003(pemcln) groups=10003(pemcln) j154163
uid=152084(k152084) gid=10003(pemcln) groups=10003(pemcln) k152084
uid=154163(b153999) gid=10003(pemcln) groups=10003(pemcln) b153999
uid=154226(u154226) gid=10003(pemcln) groups=10003(pemcln) u154226

我只想显示第一行和第三行,因为它们具有相同的重复 UID 值“154163”

我知道如何匹配整行而不是每行的子集的唯一方法。

此代码从每一行中查找 ID。如果任何 ID 出现不止一次,则打印其行:

$ awk -F'[=(]' '{cnt[]++;lines[]=lines[]"\n"[=10=]} END{for (k in cnt){if (cnt[k]>1)print lines[k]}}' file

uid=154163(j154163) gid=10003(pemcln) groups=10003(pemcln) j154163
uid=154163(b153999) gid=10003(pemcln) groups=10003(pemcln) b153999

工作原理:

  • -F'[=(]'

    awk 将输入文件分隔成记录(行),并将记录分隔成字段。在这里,我们告诉 awk 使用 =( 作为字段分隔符。这样做是为了让第二个字段成为 ID。

  • cnt[]++; lines[]=lines[]"\n"[=16=]

    对于读入的每一行,我们都会记录该 ID 出现的次数,cnt。此外,我们将与该 ID 关联的所有行保存在数组 lines.

  • END{for (k in cnt){if (cnt[k]>1)print lines[k]}}

    到达文件末尾后,我们遍历每个观察到的 ID,如果它出现不止一次,则打印它的行。

有人已经提供了一个 awk 脚本来满足您的需求,假设文件足够小以适合内存(它们存储所有行直到结束 then决定输出什么)。它没有任何问题,实际上它可以被认为是这个问题的规范 awk 解决方案。我为 awk 可能难以满足存储要求的情况提供了这个答案。

具体来说,如果您有较大的文件导致该方法出现问题,下面的 awk 脚本 myawkscript.awk 将处理它,前提是您首先对文件进行排序以便它可以依赖与事实相关的行在一起。为了确保它已排序并且您可以轻松获得相关键(使用 =( 作为字段分隔符),您将其称为:

sort <inputfile | awk -F'[=(]' -f myawkscript.awk

脚本是:

state == 0 {
    if (lastkey == ) {
        printf "%s", lastrec;
        print;
        state = 1;
    };
    lastkey = ;
    lastrec = [=11=]"\n";
    next;
}
state == 1 {
    if (lastkey == ) {
        print;
    } else {
        lastkey = ;
        lastrec = [=11=]"\n";
        state = 0;
    }
}

它基本上是一个状态机,状态 0 扫描重复项,状态 1 输出重复项。

在状态 0 中,当前行的相关部分与前一行进行比较,如果匹配,则输出两者并切换到状态 1。如果没有匹配项,它只会移至下一行。

在状态一中,它检查每一行与集合中的原始行,只要匹配就输出。当它发现一个不匹配时,它存储它并恢复到状态零。