shell中多个文件的平均值

我想计算15个文件的平均值: – ifile1.txt,ifile2.txt,…..,ifile15.txt。 每个文件的列数和行数是相同的。 部分数据看起来像

ifile1.txt ifile2.txt ifile3.txt 3 5 2 2 . 1 2 1 3 . 4 3 4 1 . 1 4 2 1 . 1 3 0 2 . 5 3 1 5 . 4 6 5 2 . 2 5 5 1 . 3 4 3 1 . 5 5 7 1 . 0 0 1 1 . 4 3 4 0 . . . . . . . . . . . . . . . . 

我想find一个新的文件,将显示这15个文件的平均值。

 ofile.txt 2.66 3.33 2.33 2 . (ie average of 3 1 4, average of 5 2 3 and so on) 2.33 3.33 1 2.66 . 3 5 4.33 1.33 . 3 2.33 4 0.66 . . . . . . 

我正在尝试跟随,但得到错误

 awk'{for (i=1; i<=NF; i++)} rows=FNR;cols=NF} END {for (i=1; i<=rows; i++){for (j=1; j<=cols; j++) s+=$i;print $0,s/NF;s=0}}' ifile* > ofile.txt 

正如所写:

 awk'{for (i=1; i<=NF; i++)} rows=FNR;cols=NF} END … 

你会得到'command not found'作为错误,因为你必须在awk和引号内的脚本之间保留一个空格。 当你解决这个问题的时候,你会开始陷入困境,因为在脚本的第一行有两个}{ }

当你解决这个问题时,你将需要一个二维数组,按行号和列号进行索引,并将文件中的值相加。 您还需要知道处理的文件数量和列数。 然后您可以安排遍历END块中的二维数组。

 awk 'FNR == 1 { nfiles++; ncols = NF } { for (i = 1; i < NF; i++) sum[FNR,i] += $i if (FNR > maxnr) maxnr = FNR } END { for (line = 1; line <= maxnr; line++) { for (col = 1; col < ncols; col++) printf " %f", sum[line,col]/nfiles; printf "\n" } }' ifile*.txt 

给出这个问题的三个数据文件:

ifile1.txt

 3 5 2 2 1 4 2 1 4 6 5 2 5 5 7 1 

ifile2.txt

 1 2 1 3 1 3 0 2 2 5 5 1 0 0 1 1 

ifile3.txt

 4 3 4 1 5 3 1 5 3 4 3 1 4 3 4 0 

我演示的脚本产生:

  2.666667 3.333333 2.333333 2.333333 3.333333 1.000000 3.000000 5.000000 4.333333 3.000000 2.666667 4.000000 

如果要将小数位数控制为2,则使用%.2f来代替%f

 $ { head -n1 ifile1.txt; paste ifile*.txt;} | awk 'NR==1{d=NF; next;} {for (i=1;i<=d;i++) {s=0; for (j=i;j<=NF;j+=d) s+=$j; printf "%.2f%s",s/(NF/d),j==NF+d?"\n":"\t";}}' 2.67 3.33 2.33 2.00 2.33 3.33 1.00 2.67 3.00 5.00 4.33 1.33 3.00 2.67 4.00 0.67 

这个脚本计算每一行并在移动到下一行之前打印结果。 因此,脚本不需要一次保存所有的数据。 如果数据文件很大,这一点很重要。

怎么运行的

  • { head -n1 ifile1.txt; paste ifile*.txt;}

    这只会打印ifile1.txt的第一行。 然后, paste命令导致它打印合并的所有文件的第一行,然后合并第二行,依此类推:

     $ paste ifile*.txt 3 5 2 2 1 2 1 3 4 3 4 1 1 4 2 1 1 3 0 2 5 3 1 5 4 6 5 2 2 5 5 1 3 4 3 1 5 5 7 1 0 0 1 1 4 3 4 0 
  • |

    管道符号使上述命令的输出作为输入发送到awk。 依次查找每个awk命令:

  • NR==1{d=NF; next;}

    对于第一行,我们将变量d的列数保存起来。 然后,我们跳过其余的命令,重新开始next行输入。

  • for (i=1;i<=d;i++) {s=0; for (j=i;j<=NF;j+=d) s+=$j; printf "%.2f%s",s/(NF/d),j==NF+d?"\n":"\t";}

    这将从各个文件中累加数字并打印平均值。

作为多行脚本:

 { head -n1 ifile1.txt paste ifile*.txt } | awk ' NR==1 {d=NF; next;} { for (i=1;i<=d;i++) { s=0; for (j=i;j<=NF;j+=d) s+=$j; printf "%.2f%s",s/(NF/d),j==NF+d?"\n":"\t"; } } 

读取原始文件时,您需要将字段总和保存到数组中。 你不能在END块访问$0i ,因为那时没有输入行。

 awk '{rows=FNR; cols=NF; for (i = 1; i <= NF; i++) { total[FNR, i] += $i }} FILENAME != lastfn { count++; lastfn = FILENAME } END { for (i = 1; i <= rows; i++) { for (j = 1; j <= cols; j++) { printf("%s ", total[i, j]/count) } printf("\n") } }' ifile* > ofile.txt