##
#
# progress_bar
#
# Input: $1 - A message to display with the progress bar. It can contain
# any of the following special 3-character strings, each of which
# will be converted to the following value:
#
# - the progress bar itself
# - the current count
# - the percentage finished
# - estimated remaining time
# - the total count
#
# $2 - A total count
# $3 - (optional) The 'done' symbol (default is '@')
# $4 - (optional) The 'todo' symbol (default is '-')
#
# Output: A closure that takes a count, and updates the progress bar based
# on its value. When the progress bar is done, it should be called
# with no arguments, to show that the progress is complete -and- to
# print a final newline.
##
sub progress_bar {
my ($msg, $total, $done_sym, $todo_sym) = @_;
($total || 0) or fatal("total can NOT be zero/undefined!");
$done_sym ||= '@';
$todo_sym ||= '-';
$| = 1;
my $start = time();
my $last = $start;
my $remain = "???";
my $b_left = ($msg =~ //)? 1: 0;
my $c_prog = sub {
my ($count) = @_;
my $b_done = defined($count)? 0: 1;
$b_done and $count = $total;
($count > $total) and $count = $total;
my $pcnt = sprintf "%6.2f", 100.0 * $count / $total;
my $new_msg = $msg;
# Calculate estimated remaining time
my $time = time();
my $dtime = $time - $start;
if ($b_done) {
$remain = "Finished";
} elsif ($b_left and $count and $dtime and $time - $last > 3) {
$last = $time;
my $rate = ($count / $dtime);
my $nsec = int(($total - $count) / $rate);
my $hr = my $min = 0;
if ($nsec > 3600) { $hr = int($nsec / 3600); $nsec -= 3600 * $hr }
if ($nsec > 60) { $min = int($nsec / 60); $nsec -= 60 * $min }
if ($hr) {
$remain = sprintf "%02d:%02d:%02d", $hr, $min, $nsec;
} elsif ($min) {
$remain = sprintf "%02d:%02d", $min, $nsec;
} else {
my $s = (1 == $nsec)? "": "s";
$remain = "$nsec second$s";
}
}
$new_msg =~ s//$count/g;
$new_msg =~ s//$pcnt/g;
$new_msg =~ s//$total/g;
$new_msg =~ s//$remain/g;
my $len = 79 + 3 - length($new_msg);
my $ndone = int($len * $count / $total);
my $ntodo = $len - $ndone;
my $bar = ($done_sym x $ndone) . ($todo_sym x $ntodo);
$new_msg =~ s//$bar/;
print "$new_msg\r";
$b_done and print "\n";
};
return $c_prog;
}
##
##
use strict;
use warnings;
my @files = ( 'some', 'list', 'of', 'files' );
my $nfiles = @files;
my $nhandled = 0;
my $c_prog = progress_bar("File # of [%] ", $nfiles);
foreach my $file (@files) {
do_something_with_this_file($file);
$c_prog->(++$nhandled);
}
$c_prog->();