Perl 使用awk对多列进行小计

Perl 使用awk对多列进行小计,perl,awk,scripting,Perl,Awk,Scripting,在没有Excel的情况下,可以使用awk对csv文件进行多列小计 File1.txt Group,Host,1-Dec-14,2-Dec-14,3-Dec-14,4-Dec-14,5-Dec-14,6-Dec-14 GroupA,HostA,10,20,30,40,50,60 GroupB,HostB,10,20,30,40,50,60 GroupA,HostC,10,20,30,40,50,60 GroupC,HostD,10,20,30,40,50,60 GroupB,HostE,10,20

在没有Excel的情况下,可以使用awk对csv文件进行多列小计

File1.txt

Group,Host,1-Dec-14,2-Dec-14,3-Dec-14,4-Dec-14,5-Dec-14,6-Dec-14
GroupA,HostA,10,20,30,40,50,60
GroupB,HostB,10,20,30,40,50,60
GroupA,HostC,10,20,30,40,50,60
GroupC,HostD,10,20,30,40,50,60
GroupB,HostE,10,20,30,40,50,60
GroupC,HostF,10,20,30,40,50,60
向各部门备案:

Group,Host,Dept,1-Dec-14,2-Dec-14,3-Dec-14,4-Dec-14,5-Dec-14,6-Dec-14
GroupA,HostA,Finance,10,20,30,40,50,60
GroupB,HostB,HR,10,20,30,40,50,60
GroupA,HostC,Finance,10,20,30,40,50,60
GroupC,HostD,HR,10,20,30,40,50,60
GroupB,HostE,Finance,10,20,30,40,50,60
GroupC,HostF,HR,10,20,30,40,50,60
结果是

Group,Host ,1-Dec-14,2-Dec-14,3-Dec-14,4-Dec-14,5-Dec-14,6-Dec-14
GroupA Total,,20,40,60,80,100,120
GroupB Total,,20,40,60,80,100,120
GroupC Total,,20,40,60,80,100,120
GrandTotal,,60,120,180,240,300,360

下面是一个可以用作模板的Perl脚本:

use strict;
use warnings;

use Text::CSV;

my $fn = 'File1.txt';
my $csv = Text::CSV->new();
open(my $fh, '<', $fn) or die "Could not open file '$fn': $!\n";
my $header = <$fh>;
my %grp; 
my @tot;
while (my $line = <$fh>) {
    chomp $line;
    if ($csv->parse($line)) {
        my @fields = $csv->fields();
        my $key = $fields[0];
        $grp{$key} //= [(0) x ( scalar(@fields) - 2 )];
        for my $i (2..$#fields) {
            $grp{$key}->[$i - 2] += $fields[$i];
            $tot[$i - 2] += $fields[$i];
        }
    } else {
        warn "Line could not be parsed: $line\n";
    }
}
close($fh);

print $header;
for (sort keys %grp) {
    print $_ . " Total,," . join( ",", @{$grp{$_}}) . "\n";
}
print "GrandTotal,," . join( ",", @tot ) . "\n";
使用严格;
使用警告;
使用Text::CSV;
my$fn='File1.txt';
my$csv=Text::csv->new();

open(my$fh,“此程序按您的要求执行。它使用通常的策略,将数据以适当的形式读入内存,然后将数据打印到输出

目前,它从
数据中读取输入
,并将输出发送到
标准输出
。我相信您知道如何打开自己的输入和输出文件?可以使用命令行上的重定向
将输出保存到您喜欢的任何文件中,但如果您需要更多帮助,请说明

use strict;
use warnings;

print scalar <>;  # Copy and ignore the header

my (%data, @groups, @grand);

while (<>) {
  chomp;
  my ($group, $host, @vals) = split /,/;
  push @groups, $group unless $data{$group};
  for my $i (0 .. $#vals) {
    $data{$group}[$i] += $vals[$i];
    $grand[$i] += $vals[$i];
  }
}

for my $group (@groups) {
  print join(',', $group, '', @{ $data{$group} }), "\n";
}

print join(',', 'GrandTotal', '', @grand), "\n";

使用GNU awk实现真正的二维阵列:

$ cat tst.awk
BEGIN{ FS=OFS="," }
NR==1 { print; next }
{
    for (i=3; i<=NF; i++) {
        subtot[$1][i] += $i
    }
}
END {
    for (group in subtot) {
        printf "%s%s", group, OFS
        for (i=3; i<=NF; i++) {
            printf "%s%s", OFS, subtot[group][i]
        }
        print ""
    }
}
$
$ awk -f tst.awk file
Group,Host,1-Dec-14,2-Dec-14,3-Dec-14,4-Dec-14,5-Dec-14,6-Dec-14
GroupA,,20,40,60,80,100,120
GroupB,,20,40,60,80,100,120
GroupC,,20,40,60,80,100,120
$cat tst.awk
开始{FS=OFS=“,”}
NR==1{打印;下一个}
{

用于(i=3;i使用tee+mkfifo复制管道输出。用于大型文件

$>cat foo 
foo,10,foo_a
bar,10,bar_a
foo,20,foo_b
bar,20,bar_b
foo,69,foo_c
bar,69,bar_c

$>mkfifo fifotmp

$>awk -F',' ' 
BEGIN {T="\t" }

pass==1 {
    sum_first_col[ $1 ] += $2;
    sum_all += $2;
} 

pass==2 { 
    percentage_itm=100 * $2 / sum_first_col[ $1 ];
    percentage_all=100 * $2 / sum_all;

    print $1 T $2 T $3 T percentage_itm T percentage_all;

}' pass=1 <(cat foo | tee -a fifotmp) pass=2 <(cat fifotmp ) 

OR 

$>cat foo | tee -a fifotmp | awk -F',' ' 
    BEGIN {T="\t" }

    pass==1 {
        sum_first_col[ $1 ] += $2;
        sum_all += $2;
    } 

    pass==2 { 
        percentage_itm=100 * $2 / sum_first_col[ $1 ];
        percentage_all=100 * $2 / sum_all;

        print $1 T $2 T $3 T percentage_itm T percentage_all;

    }' pass=1 - pass=2 <(cat fifotmp ) 

foo 10  foo_a   10.101  5.05051
bar 10  bar_a   10.101  5.05051
foo 20  foo_b   20.202  10.101
bar 20  bar_b   20.202  10.101
foo 69  foo_c   69.697  34.8485
bar 69  bar_c   69.697  34.8485
$>猫食
富,10,富阿
巴,10巴,巴
富,20,富
巴,20巴,巴
富,69,富
巴,69,巴
$>mkfifo-fifotmp
$>awk-F','
开始{T=“\T”}
通过==1{
第一列总和[$1]+=$2;
总和=2美元;
} 
通过==2{
百分比=100*$2/第一列总和[$1];
百分比=100*$2/总和;
打印$1 T$2 T$3 T百分比所有;

}'pass=1当然有可能。使用关联数组。@Barmar awk-F,{a[$1]+=$3;}END{for(a中的i)print i”,“a[i];}'上面的代码用于第三列,如何对多个列执行相同的操作,而不在数组中特别提到列ref,也不包括文件
NR>1中的头
跳过第一行为什么在这个问题上有
Perl
标记?您只要求了
awk
解决方案实际上不需要ei在
chomp
$grp{$key}/=[(0)x(标量(@fields)-2)]
。此外,
Text::CSV->error\u diag
返回输入中出错的字段,这可能比整行有用得多。在我的解决方案中,
chomp
是必需的,因为我只需
拆分
每行,如果没有
chomp
最后一个字段将以新行结尾。在您的情况下,
Text::CSV
将为您删除它。非常欢迎您。另存为小计并作为perl小计执行,但有几个错误无法在@INC中找到Text/CSV.pm(@INC-contains:/usr/local/lib64/perl5/usr/local/share/perl5/perl5/usr/share/perl5)在第4行。BEGIN失败——编译在第4行中止。您必须安装模块
Text::CSV
。如何输入所有它尝试过/usr/bin/perl-MCPAN-e'install Text::CSV_XS',但仍然是相同的错误啊,得到了它,将上面的代码保存为sub并作为perl sub运行,但每次我们都需要将数据保存在包含代码的同一文件中吗?好的,我假设你你会知道如何用Perl打开一个文件。我已经更改了我的解决方案,因此它希望输入文件的路径作为命令行上的一个参数,因此,假设你将程序文件命名为
group_totals.pl
,你可以使用
group_totals.pl input.csv>totals.csv
,它将从
input.csv
获取输入,然后运行它将输出写入
totals.csv
如果我在第三列中添加了另一列department,代码需要如何定制,只是为了了解我是否添加更多列以及是否要小计获取错误awk:tst.awk:5:subtomt[$1][i]+=$i awk:tst.awk:5:^语法错误awk:tst.awk:12:printf“%s%s”,OFS,subtomt[group][i]awk:tst.awk:12:^语法错误如果您没有按照要求使用GNU awk(或者使用的是不支持2D数组的非常旧的版本).gawk-3.1.7-10.el6.x86_64安装在pc中,上面的脚本支持的版本是什么?这是一个非常非常古老的gawk。gawk现在在4.1.1上,真正的多维数组(“数组的数组”)和大量其他有用的功能从4.0开始得到支持,请参阅。
$>cat foo 
foo,10,foo_a
bar,10,bar_a
foo,20,foo_b
bar,20,bar_b
foo,69,foo_c
bar,69,bar_c

$>mkfifo fifotmp

$>awk -F',' ' 
BEGIN {T="\t" }

pass==1 {
    sum_first_col[ $1 ] += $2;
    sum_all += $2;
} 

pass==2 { 
    percentage_itm=100 * $2 / sum_first_col[ $1 ];
    percentage_all=100 * $2 / sum_all;

    print $1 T $2 T $3 T percentage_itm T percentage_all;

}' pass=1 <(cat foo | tee -a fifotmp) pass=2 <(cat fifotmp ) 

OR 

$>cat foo | tee -a fifotmp | awk -F',' ' 
    BEGIN {T="\t" }

    pass==1 {
        sum_first_col[ $1 ] += $2;
        sum_all += $2;
    } 

    pass==2 { 
        percentage_itm=100 * $2 / sum_first_col[ $1 ];
        percentage_all=100 * $2 / sum_all;

        print $1 T $2 T $3 T percentage_itm T percentage_all;

    }' pass=1 - pass=2 <(cat fifotmp ) 

foo 10  foo_a   10.101  5.05051
bar 10  bar_a   10.101  5.05051
foo 20  foo_b   20.202  10.101
bar 20  bar_b   20.202  10.101
foo 69  foo_c   69.697  34.8485
bar 69  bar_c   69.697  34.8485