Bash 合并除关键点字段外所有相同的行,并使关键点字段成为一个范围

Bash 合并除关键点字段外所有相同的行,并使关键点字段成为一个范围,bash,merge,scripting,combine,Bash,Merge,Scripting,Combine,我已经看了很多帖子,还没有找到我想要的。我不确定如何获取以下样本数据: host1 input nic1 ip1 ip2 PROT 30000 10 host1 input nic1 ip1 ip2 PROT 40000 10 host1 input nic1 ip1 ip2 PROT 50000 10 host1 input nic1 ip1 ip2 PROT 60000 10 host1 in

我已经看了很多帖子,还没有找到我想要的。我不确定如何获取以下样本数据:

host1   input   nic1    ip1 ip2 PROT    30000   10
host1   input   nic1    ip1 ip2 PROT    40000   10
host1   input   nic1    ip1 ip2 PROT    50000   10
host1   input   nic1    ip1 ip2 PROT    60000   10
host1   input   nic1    ip3 ip2 PROT    10      30000
host1   input   nic1    ip3 ip2 PROT    10      40000
host1   input   nic1    ip3 ip2 PROT    10      50000
host1   input   nic1    ip3 ip2 PROT    10      60000
host1   output  nic1    ip2 ip1 PROT    10      30000
host1   output  nic1    ip2 ip1 PROT    10      40000
host1   output  nic1    ip2 ip1 PROT    10      50000
host1   output  nic1    ip2 ip1 PROT    10      60000
host1   output  nic1    ip2 ip3 PROT    30000   10
host1   output  nic1    ip2 ip3 PROT    40000   10
host1   output  nic1    ip2 ip3 PROT    50000   10
host1   output  nic1    ip2 ip3 PROT    60000   10
host1   output  loc     ip2 ip2 PROT    10      30000
host1   output  loc     ip2 ip2 PROT    10      50000
并将其合并为:

host1   input   nic1    ip1 ip2 PROT    30000:60000 10
host1   input   nic1    ip3 ip2 PROT    10          30000:60000
host1   output  nic1    ip2 ip1 PROT    10          30000:60000
host1   output  nic1    ip2 ip3 PROT    30000:60000 10
host1   output  loc     ip2 ip2 PROT    10          30000:50000
我有大量这样的数据,需要为给定行的多个字段设置范围,但我认为如果有人能告诉我如何为一个字段设置范围,就像我上面所说的,我应该能够找出其余的字段。如果没有,我会跟进:)。提前感谢您的帮助。

更新 我已经重构了下面答案中的代码,以使其更具可读性。主体应阅读几乎所有的英文散文

#!/usr/bin/awk -f
# main body
NR == 1 {
  copyRecordTo(veryold)
  next
}
{
  if (inSameGroup()) {
    copyRecordTo(old)
  } else {
    makeRangeForField(NF - 1)
    makeRangeForField(NF)
    nicePrint()
    copyRecordTo(veryold)
  }
}
END {
  makeRangeForField(NF - 1)
  makeRangeForField(NF)
  nicePrint()
}

# functions
function copyRecordTo(line) {
  for (i = 1; i <= NF; ++i) line[i] = $i
}
function nicePrint() {
  for (i = 1; i <= NF; ++i) {
    i == NF - 1 ? fmt = "%s\t\t" : fmt = "%s\t"
    printf(fmt, old[i])
  }
  printf("\n")
}
function makeRangeForField(f) {
  if (old[f] != veryold[f])
    old[f] = veryold[f]":"old[f]
}
function inSameGroup() {
  b = 1
  for (i = 1; i <= NF - 2; ++i)
    b *= $i == veryold[i]
  return b == 1
}

这里有一个
awk
脚本的可爱杰作。仅提及使用
env
执行
awk
解释器的备用shebang,无论其路径如何:
#/usr/bin/env--拆分字符串awk--文件
或缩写:
#/usr/bin/env-S awk-f
@LéaGris我认为这个脚本过于复杂,但我可以在凌晨1点使用它。关于shebang,它是否仅限于一个参数?
env--split string
请参见:
-S/--split string在脚本中的用法,-S选项允许在脚本中指定多个参数。运行名为1.pl的脚本,该脚本包含以下第一行:#/usr/bin/env-S perl-w-T
。因此,它将任意数量的空格分隔参数传递给解释器。在
NR==1
规则中,您可以添加
next
作为最终命令,然后删除
NR>1
条件。(不管怎样——干得好)@DavidC.Rankin,谢谢!我还对代码进行了重构,使其更具可读性(至少我发现它更具可读性)。当你说大量数据超出内存容量时?
datamash groupby 1,2,3,4,5,6 min 7 max 7 min 8 max 8
?@dawg抱歉,我只说几百MB的文本文件,所以,记忆不是一个问题issue@oguzismail我将使用datamash,但我确实需要一些linux本机的东西,比如目前提供的awk脚本。非常感谢您的建议。@oguzismail感谢您对datamash的介绍!它确实输出了非常接近我需要的结果,但遗憾的是,我需要一个不需要在Linux中安装额外软件的解决方案
#!/usr/bin/awk -f
NR == 1 {
  for (i = 2; i <= NF; ++i) {
    veryold[i] = $i
  }
  next
}
{
  b = 1
  for (i = 2; i <= NF - 2; ++i) {
    b *= $i == veryold[i]
  }
  if (b == 1) {
    for (i = 1; i <= NF; ++i) {
      old[i] = $i
    }
  } else {
    if (old[NF - 1] != veryold[NF - 1]) {
      old[NF - 1] = veryold[NF - 1]":"old[NF - 1]
    }
    if (old[NF] != veryold[NF]) {
      old[NF] = veryold[NF]":"old[NF]
    }
    for (i = 1; i <= NF; ++i) {
      if (i == NF - 1) {
        fmt = "%s\t\t"
      } else {
        fmt = "%s\t"
      }
      printf(fmt, old[i])
    }
    printf("\n")
    for (i = 2; i <= NF; ++i) {
      veryold[i] = $i
    }
  }
}
END {
  if (old[NF - 1] != veryold[NF - 1]) {
    old[NF - 1] = veryold[NF - 1]":"old[NF - 1]
  }
  if (old[NF] != veryold[NF]) {
    old[NF] = veryold[NF]":"old[NF]
  }
  for (i = 1; i <= NF; ++i) {
    if (i == NF - 1) {
      fmt = "%s\t\t"
    } else {
      fmt = "%s\t"
    }
    printf(fmt, old[i])
  }
}