#!/usr/bin/perl # subtotal.pl -- Subtotal arbitrary line items #$ver = "v1.0"; # 2003-06-10 JP Vossen $ver = "v1.1"; # 2003-06-26 JPV Added delimtier swap in header output ########################################################################## (($myname = $0) =~ s/^.*(\/|\\)//ig); # remove up to last "\" or "/" $Greeting = ("$myname $ver Copyright 2003 JP Vossen (http://www.jpsdomain.org/)\n"); $Greeting .= (" Licensed under the GNU GENERAL PUBLIC LICENSE:\n"); $Greeting .= (" See http://www.gnu.org/copyleft/gpl.html for full text and details.\n"); if (("@ARGV" =~ /\?/) || ("@ARGV" =~ /-h/) || "@ARGV" =~ /--help/) { print STDERR ("\n$Greeting\n\n"); print STDERR <<"EoN"; # Usage notes Usage: $myname [OPTIONS] (-i [FILE]) (-o [FILE] | -W) (-q) -i {infile} = Use infile as the input file, otherwise use STDIN. -o {outfile} = Use outfile as the output file, otherwise use STDOUT. -d {delimiter} = Split input fields on {delimiter}, default is TAB. -D {delimiter} = Use {delimiter} between output fields, default is TAB. -s {lines} = Skip # of {lines} at the top of the file (i.e. header). -q = Be quiet about it. -B = Print deBug messages to STDERR. Subtotal arbitrary line items. Assumes that the number to subtotal is the first field and that the rest of the line is the keyfield (i.e. hash value). EoN die ("\n"); } # end of usage use Getopt::Std; # Use Perl5 built-in program argument handler getopts('i:o:d:D:s:qB'); # Define possible args. # Set defaults $delimiter_in = $opt_d || "\t"; $delimiter_out = $opt_D || "\t"; if (! $opt_i) { $opt_i = "-"; } # If no input file specified, use STDIN if (! $opt_o) { $opt_o = "-"; } # If no output file specified, use STDOUT open (INFILE, "$opt_i") or die ("$myname: error opening '$opt_i' for input: $!\n"); open (OUTFILE, ">$opt_o") or die ("$myname: error opening '$opt_o' for output: $!\n"); if (! $opt_q) { print STDERR ("\n$Greeting\n"); } if ($opt_s) { # Skip # of {lines} at the top of the file (i.e. header). for ($i;$i<$opt_s;$i++) { # For the specified number of header lines $aline = ; # Grab a line chomp($aline); # chomp it @header[$i] = $aline; # Assign it to an array if ($opt_B) { warn ("Header line ~$i~ = ~@header[$i]~\n"); } } # end of for loop } # end of skip header while ($aline = ) { chomp($aline); next if $aline =~ m/^\s*$/; # Skip blank lines next if $aline !~ m/^\d+/; # Skip lines that don't start with a number # Note -1 "chunking" to get trailing empty fields! @arecord = split(/$delimiter_in/, $aline, -1); # Build a new scalar "record" without the item count $hashrec = join($delimiter_out,splice (@arecord, 1)); # Remove anything that is not a number or decimal. $arecord[0] =~ s/[^\d.]//g; # Add the count to the subtotal hash $subtotal{$hashrec} += $arecord[0]; # Keep a grand total also $grandtotal += $arecord[0]; if ($opt_B) { warn ("count = ~$arecord[0]~\thashrec = ~$hashrec~\n"); } } # end of while input # If we have a header if ($opt_s) { foreach $aline (@header) { $aline =~ s/$delimiter_in/$delimiter_out/g; # Swap delimiters print OUTFILE ("$aline\n"); } # end of foreach header line } # end of skip header # Write the output lines, sorted by hash VALUE foreach $item (sort { $subtotal{$b} <=> $subtotal{$a} } keys %subtotal) { print OUTFILE ("$subtotal{$item}", $delimiter_out, "$item\n"); } # end of foreach output # Write the grand total print OUTFILE ("$grandtotal${delimiter_out}Grand Total\n"); if (! $opt_q) { print STDERR ("\n\a$myname finished in ",time()-$^T," seconds.\n"); } ##########################################################################