Awk:在具有相同列布局的多个文件中汇总列值

时间:2017-06-16 20:28:07

标签: linux awk gawk

我有许多具有相同标题的文件:

COL1,COL2,COL3,COL4

您可以忽略COL1-COL3。 COL4包含一个数字。每个文件包含大约200行。我试图总结各行。例如:

档案1

COL1 COL2 COL3 COL4
 x     y    z   5 
 a     b    c   10 

文件2

COL1 COL2 COL3 COL4
 x     y    z   8 
 a     b    c   14

然后返回一个新文件:

{{1}}

有没有没有 AWK的简单方法?如果需要,我会使用AWK,我只是觉得可能有一个简单的单行程,我可以立即运行。我想到的AWK脚本感觉有点长。

由于

5 个答案:

答案 0 :(得分:2)

如果所有文件都具有相同的标题 - awk 解决方案:

awk '!f && FNR==1{ f=1; print $0 }FNR>1{ s[FNR]+=$NF; $NF=""; r[FNR]=$0 }
      END{ for(i=2;i<=FNR;i++) print r[i],s[i] }' File[12]

输出(2个文件):

COL1 COL2 COL3 COL4
x y z 8
a b c 14

此方法可应用于多个文件(在这种情况下,您可以为文件名扩展指定 globbing File*

答案 1 :(得分:2)

pasteawk 结合起来,就像Kristo Mägi's answer一样,是您最好的选择:

  • paste合并输入文件中的相应行
  • 将单个输入行流发送到awk,每个输入行包含要汇总的所有字段。

假设输入文件和列的数量固定,Kristo的答案可以简化以使处理更加高效:

paste file1 file2 | awk '{ print $1, $2, $3, (NR==1 ? $4 : $4 + $8) }'

注意:上面会生成 space - 分隔的输出列,因为awk的输出字段分隔符OFS的默认值是是一个单一的空间。

假设所有文件具有相同的列结构和行数,下面是解决方案的概括,其中:

  • 概括为超过2个输入文件(以及超过2个数据行)
  • 概括为任意数量的字段,只要总结的字段是 last
#!/bin/bash

files=( file1 file2 ) # array of input files
paste "${files[@]}" | awk -v numFiles=${#files[@]} -v OFS='\t' '
  {
    row = sep = ""
    for(i=1; i < NF/numFiles; ++i) { row = row sep $i; sep = OFS }
    sum = $(NF/numFiles) # last header col. / (1st) data col. to sum
    if (NR > 1) { for(i=2; i<=numFiles; ++i) sum += $(NF/numFiles * i) } # add other cols.
    printf "%s%s%s\n", row, OFS, sum
  }
'

请注意,\t(标签字符。)用于分隔输出字段,并且由于依赖awk的默认行拆分字段,保留了确切的输入字段之间的空白无法保证。

答案 2 :(得分:1)

你说你有很多文件&#34;。即超过2.

鉴于这3个文件(并且可以使用任何数字):

$ cat f1 f2 f3
COL1 COL2 COL3 COL4
 x    y   z    3
 a    b   c    4
COL1 COL2 COL3 COL4
 x     y    z   5 
 a     b    c   10 
COL1 COL2 COL3 COL4
 x     y    z   10 
 a     b    c   15 

你可以这样做:

$ awk 'FNR==1{next}
     {sum[$1]+=$4}
     END{print "COL1 COL4"; 
         for (e in sum) print e, sum[e]} ' f1 f2 f3
COL1 COL4
x 18
a 29

目前还不清楚你打算用COL2或COL3做什么,所以我没有添加它。

答案 3 :(得分:1)

还有一个选择。

命令:

paste f{1,2}.txt | sed '1d' | awk '{print $1,$2,$3,$4+$8}' | awk 'BEGIN{print "COL1","COL2","COL3","COL4"}1'

结果:

COL1 COL2 COL3 COL4
x y z 8
a b c 14

它的作用:

测试文件:

$ cat f1.txt
COL1 COL2 COL3 COL4
 x    y   z    3
 a    b   c    4

$ cat f2.txt
COL1 COL2 COL3 COL4
 x     y    z   5
 a     b    c   10

命令:paste f{1,2}.txt
连接2个文件并输出:

COL1 COL2 COL3 COL4 COL1 COL2 COL3 COL4
 x    y   z    3     x     y    z   5
 a    b   c    4     a     b    c   10

命令:sed '1d'
意味着暂时删除标题

命令:awk '{print $1,$2,$3,$4+$8}'
返回COL1-3并从粘贴结果中总结$ 4和$ 8。

命令:awk 'BEGIN{print "COL1","COL2","COL3","COL4"}1'
添加标题

编辑:
关注@ mklement0评论,他对标题处理是正确的,因为我忘记了NR==1部分。

所以,我也会在这里代理他的更新版本:

paste f{1,2}.txt | awk '{ print $1, $2, $3, (NR==1 ? $4 : $4 + $8) }'

答案 4 :(得分:0)

$ awk '
     NR==1 { print }
     { sum[FNR]+=$NF; sub(/[^[:space:]]+[[:space:]]*$/,""); pfx[FNR]=$0 }
     END { for(i=2;i<=FNR;i++) print pfx[i] sum[i] }
' file1 file2
COL1 COL2 COL3 COL4
 x     y    z   8
 a     b    c   14

上述任何UNIX系统上的任何awk都可以健壮有效地工作,具有任意数量的输入文件和这些文件的任何内容。唯一可能的问题是它必须在内存中保留相当于1个这样的文件,所以如果每个文件都非常庞大,那么你可能会耗尽可用内存。