【问题标题】:Comparing multiple columns from the same file比较同一文件中的多个列
【发布时间】:2018-08-16 14:44:28
【问题描述】:

我想比较同一个文件中的多列,看看我在哪一行有差异,在本例中是前 4 列。该文件如下所示:

           Column1 Column2 Column3 Column4 Column5
              AA      AA      AA      AA    AA
              GG      GG      GG      GG    GG
              CC      GC      CC      CC    CC
              CC      TT      CC      GC    TT

如果我做了这样的事情:

 awk -F"\t" '{if ($1==$2) print $1, $2; else print NR}' file.txt 

我得到下一个输出:

             1
             AA      AA 
             GG      GG 
             4
             5

但是,我如何使用简单的语法同时比较前 4 列?我尝试过这样的事情:

       awk -F"\t" '{if ([$2,$3,$4]==$1) print $1, $2, $3, $4 ; else print NR}' File.txt

但它不起作用。

我的预期输出应该是:

              1 
              AA      AA      AA      AA
              GG      GG      GG      GG
              4
              5

我的文件有超过四列,所以我想比较列组,在这种情况下是从第 1 列到第 4 列,如果它们具有相同的字符,则只打印这四列,如果它们具有相同的字符,则打印行数有区别。

我怎样才能做到这一点?

【问题讨论】:

  • 请在您的帖子中提及预期的输出。
  • 完成,感谢您的建议。
  • 不清楚 - 您是要打印在每一列中具有相同值的行,还是要打印在所有列中与其他行或其他内容具有相同值的行?输出中的这些数字是什么?
  • 我正在尝试打印 4 列,如果它们具有相同的字符,如果它们没有相同的字符,我想打印行号。
  • @EricGonzález 是输入文件每行部分开头的空格,还是只是在发布数据时遇到的格式化问题?

标签: bash awk


【解决方案1】:

我会使用循环来逐步浏览您的列:

$ awk '{x=0;for(i=1;i<NF;i++){if($i!=$(i+1)){x=1}} print x?NR:$0}' input.txt

或者,为了便于阅读:

{
  x=0                     # initialize the flag for each line
  for (i=1;i<NF;i++) {    # step through the fields
    if ($i!=$(i+1)) {     # test for a match with the next field
      x=1                 # and set a flag if we see different content.
    }
  }
  print x ? NR : $0       # print NR or the first field depending on flag value
}

这将遍历所有字段(不仅仅是四个),将每个字段的内容与其后字段的内容进行比较。如果在任何时候字段不匹配,则会设置一个标志。

最终语句中的三元运算符根据标志选择要显示的输出。

请注意,您的预期输出似乎从行首修剪了空格。如果你真的想要这个,你可以在print语句之前sub(/^[[:space:]]+/,"")

【讨论】:

  • 您可能希望将$i!= 更改为($i"")!=,这样5.0 就不会被视为与5005 相同。我只是假设在这种情况下 OP 需要字符串而不是数字比较。顺便说一句,由于在某些情况下(尤其是 OSX/BSD awk),某些 awk 会因语法错误而失败,因此最好总是给它们加上括号,例如print (x ? NR : $0), - 即使不是绝对必要,它也能提高可读性。
  • 非常感谢。如果我的文件有超过 4 列,并且我只想比较第 1 列和第 4 列,并且如果我们有匹配项,则只打印这四列,或者如果我没有匹配项,则只打印行数。我怎么能做到?
  • 第 1 步将发布一个关于 THAT 的问题/示例,而不是您发布的要比较所有列的问题/示例。
  • 抱歉,我将问题编辑得更清楚。
【解决方案2】:

这可能是您正在寻找的:

$ awk '{
    sub(/^[[:space:]]+/,"")
    delete uniq
    for (i=1;i<=4;i++) {
        uniq[$i]
        out=(i>1 ? out OFS : "") $i
    }
    print (length(uniq) > 1 ? NR : out)
}' file
1
AA AA AA AA
GG GG GG GG
4
5

这需要一个 awk 来获取带有 length(array) 的数组中的元素数量,而 GNU awk 可以,我不确定其他人。

假设您确实有一个支持 length(array) 的 awk,那么无论您是这样做还是使用 @ghoti's approach 比较相邻值,只有在您在某些时候需要计算唯一值(只有我提供)而不是不仅仅是关于是否存在 1 个或多个唯一值(两者都提供)的是/否。例如,如果您想在打印行号后打印每行中存在多少个不唯一的唯一值:

awk '{
    sub(/^[[:space:]]+/,"")
    delete uniq
    for (i=1;i<=4;i++) {
        uniq[$i]
        out=(i>1 ? out OFS : "") $i
    }
    numUniq = length(uniq)
    print (numUniq > 1 ? NR OFS numUniq : out)
}' file
1 4
AA AA AA AA
GG GG GG GG
4 2
5 3

顺便说一句,如果您使用的 awk 不支持 length(array),您可以自己编写:

function alength(a, i,c) {for (i in a) ++c; return c+0}

只需将其称为alength(array)

【讨论】:

    【解决方案3】:

    如果您使用关联数组,并在执行线性传递时增加每个值的计数。你可以简单地:

    $ awk '{delete x; for(i=1;i<=NF;i++) x[$i]++; if (x[$NF]==NF) print $0; else print NR;}' file.txt
    1
    AA AA AA AA
    GG GG GG GG
    4
    5
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-27
      • 2018-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-23
      • 2015-10-22
      相关资源
      最近更新 更多