【问题标题】:How can I sort lines that have same number of fields but different in value of one field uniquely using sort or awk?如何使用 sort 或 awk 对具有相同数量的字段但一个字段的值不同的行进行排序?
【发布时间】:2020-11-03 14:07:05
【问题描述】:

我想在相同的字段位置表征具有相同字段但字段值不同的行,相同并且仅选择一行。

输入示例

让字段分隔符为“/”

1. abc/def/gh/ij/kl
2. abc/def/gh/ij/yi
3. abc/def/gh/ij/ti
4  abc/def/gh/hk/kl/oi/uh
5. abc/def/gh/ol/kl/oi/uh
6. abc/def/gh/er/kl/oi/uh
7. abc/def/gh/er/kl

将第 1,2,3 行视为相同,仅选择 1 行,即使它们的第 5 个字段的值不同,但它们与其他字段的值相同,并且具有相等的字段。

将第 4,5,6 行视为相同,仅从中选择 1 行,即使它们的第 4 个字段的值不同,但它们与其他字段的值相同,并且具有相等的字段。

第 6 行和第 7 行不一样,因为它们的字段数不相等。

期望的输出

abc/def/gh/ij/kl
abc/def/gh/hk/kl/oi/uh

注意 : 该列表有不同字段编号的行。

我试过sort -u,但显然没有用,因为排序不使用分隔符。 awk 能做到吗?

【问题讨论】:

  • 1. 2. 是输入文件的一部分吗?步骤: 1. 使用sed 或awk 对文件进行预处理以提取您要排序的部分。 2. 对此进行排序 3. 将所有应该唯一的内容放在第二列中。 4. uniq -f1 5. 删除第二列。 Can awk achieve this?是的,你试过什么?
  • @Trendingmike: sort 有选项-t,您可以使用它指定字段分隔符,还有选项-k,您可以使用它指定要使用的字段。如果您不想保留任意一个重复行,而是始终保留第一行,请查看选项-s。
  • @Trendingmike :如果您真的想要坚持使用 bash 和排序,请使用称为 Schwartzian transform 的方法。您首先创建一个新文件,其中包含一个包含唯一标识您的 unqueness 的键的附加字段,然后按此键排序,最后在已排序的文件中删除此字段。在任何情况下,您都需要提供两行何时相等的定义;到目前为止,您只发布了示例。
  • 这三行应该发生什么:a/b/cz/b/cz/x/c?
  • @EdMorton:那是个错误,我已经纠正了。感谢您发现它。

标签: bash sorting awk gnu


【解决方案1】:
$ awk -F'/' '!seen[NF]++' file
abc/def/gh/ij/kl
abc/def/gh/hk/kl/oi/uh

如果这还不是您所需要的,那么编辑您的问题以阐明您的要求并更新您的示例以包含这不起作用的行。

【讨论】:

  • 感谢 Ed Morton,非常简单而完美的答案。它为我解决了这个难题。
【解决方案2】:
$ awk -F/ 'function comb(i) {k=""; 
                             for(j=1  ;j<i  ;j++) k=k FS $j; 
                             for(j=i+1;j<=NF;j++) k=k FS $j; 
                             return k}

           !a[$0] {a[$0]; 
                   for(i=1;i<=NF;i++) if(a[comb(i)]) next; 
                   print; 
                   for(i=1;i<=NF;i++) a[comb(i)]++}' file

abc/def/gh/ij/kl
abc/def/gh/hk/kl/oi/uh

在哈希数组中,检查所有缺少一个字段的字段组合,如果没有找到则添加所有缺少一个字段的组合。还要添加琐碎案例的完整记录。

【讨论】:

    【解决方案3】:

    根据我的评论,不包括第 7 行,这可以在 gawk 中完成:

    gawk -F "/" '
       BEGIN{ a[0][0]=""; }{ if (a[$1$2$3][NF]!=null)
                    {
                    }
                    else {
                            a[$1$2$3][NF]=$0;
                            }
            }
       END{ delete a[0][0]; 
            for(i in a){
            for(j in a[i]) {
                    print a[i][j]
            }}
        }' input
    

    输出:

    abc/def/gh/ij/kl
    abc/def/gh/hk/kl/oi/uh
    

    编辑:因为第 7 行也需要在结果中?:

    #!/bin/bash
    gawk -F "/" '
    NF!=p{
       delete a[0];
       for(i in a){
            print a[i]
       }
       p=NF;
       delete a;
       a[0]="";
    }
    {
       if (p==0) p=NF;
       if (a[$1$2$3]==null)
       {
          a[$1$2$3]=$0;
       };
    }
    END {
      for (i in a) {
              print a[i];
      }
    }'
    

    输出:

    abc/def/gh/ij/kl
    abc/def/gh/hk/kl/oi/uh
    abc/def/gh/er/kl
    

    (短)解释:

    NF!=p如果字段数发生变化,打印到目前为止的结果。

    a[$1$2$3] 是结果的键。现在我确实看到这不需要是一个数组,因为我只有最大 1 个结果(每个字段数)

    END{}打印最后的结果。

    【讨论】:

    • 此答案是否适用于具有 2 个字段的行和具有 10 个字段的行?它只是特定于上面的例子吗?
    • 您确定null 支票吗?
    • @EdMorton:我使用 WIN+。在我的 Windows 系统上... (Win-key + dot ) link
    • @Luuk,试试notNull,效果一样;但这是非常不正统的检查方式,您可能是指!($1$2$3 in a)
    • @Luuk null 在您的脚本中是一个未初始化的变量(可以命名为 fluffy 并且行为相同),因此值 zero-or-null 和所以 array[index]==null 只是测试 array[index] 的 b>值 为零或 ""。专门测试空字符串是array[index]=="",但测试数组中是否存在索引的方法是idx in array,而不是array[idx]!=""(或相反!(idx in array) vs array[idx]=="")。
    【解决方案4】:

    鉴于您在评论中对相等的定义(实际上应该进入问题而不是评论),由于数学原因,您的问题无法解决:

    对你来说,两条线是相等的,如果它们最多在一个字段中不同的话。

    因此,对于您/a/b/c/d == /a/b/c/e,因为它们仅在最后一个字段中有所不同。出于同样的原因,/a/b/c/e == /a/x/c/e,因为它们也只是在一个领域(第二个领域)有所不同。

    但是对于可排序的集合,顺序关系必须是全序,这意味着相等必须是可传递的。 IE。对于任何三元组 X、Y、Z,X==Y 和 Y==Z 必须暗示 X==Z。

    让我们为您的情况做好准备

    X=/a/b/c/d
    Y=/a/b/c/e
    Z=/a/x/c/e
    

    totalorder 的要求需要X==Z,即/a/b/c/d == /a/x/c/e,但它们在两个字段中不同,而不是一个。

    【讨论】:

      猜你喜欢
      • 2018-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-23
      • 2014-08-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多