Perl 使用awk对多列进行小计
在没有Excel的情况下,可以使用awk对csv文件进行多列小计 File1.txtPerl 使用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
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