Skip to content

Instantly share code, notes, and snippets.

@sebkirche
Last active October 21, 2022 17:37
Show Gist options
  • Save sebkirche/b371e82be413c49afb184361a8738d1e to your computer and use it in GitHub Desktop.
Save sebkirche/b371e82be413c49afb184361a8738d1e to your computer and use it in GitHub Desktop.
simple pure Perl pipe viewer similar to pv. hacked to replace Number::Bytes::Human::format_bytes by a simpler formatter
#!/usr/bin/env perl
=encoding utf8
=head1 NAME
Pure perl pipe viewer
=head1 VERSION
Version 1.3
=cut
our $VERSION = '1.3';
=head1 SYNOPSIS
This is a pure perl pipe viewer to view progress of data being piped through STDIN to STDOUT. It is similar to the 'pv' command line tool.
Why? I needed 'pv' on MacOS and didn't want to bother installing ports.
=head1 USAGE
./ppv.pl <INPUTFILE >OUTPUTFILE
cat BIGFILE | gzip | ./ppv.pl | ssh somehost "gunzip | someothercomand >blargh"
=head1 AUTHORS
Mark Steele, C<< <mark at control-alt-del.org> >>
Sébastien Kirche, C<< <sebastien.kirche at free.fr> >>
=cut
use IO::Select;
use Time::HiRes qw(time);
# use Number::Bytes::Human qw(format_bytes);
use strict;
if ($#ARGV > -1 && ($ARGV[0] eq '-h' || $ARGV[0] eq '--help')){
print <<"USAGE";
Pure Perl Pipe Viewer v$VERSION Mark Steele & Sebastien Kirche
Pipe STDIN to STDOUT and display some statistics about the rate of flow.
Examples: $0 < some_file | command
some_command | $0 | other_command
nc some_host some port | $0 > some_file
USAGE
exit 0;
}
my $s = IO::Select->new();
my $size = (stat(STDIN))[7];
$s->add(\*STDIN);
my $count = 0;
my $bytes = 0;
my $time = my $start = time();
my $totalbytes = 0;
my $BAR_LENGTH = 35;
while ($s->can_read()) {
my $ret = sysread(STDIN,my $buf, 32768); # read chunks of 32kB
if ($ret == 0) {
total();
}
my $now = time();
$bytes += $ret;
$totalbytes += $ret;
if (($now - $time) >= 0) {
if ($size){
# total size is known
my $msg = sprintf "\r\rThroughput: %9s/sec, total transferred: %9s of %9s (%.02f\%) ",
humanize_bytes($bytes/($now-$time)),
humanize_bytes($totalbytes),
humanize_bytes($size),
($totalbytes/$size)*100;
print STDERR $msg;
print STDERR progress_bar($totalbytes, $size, $BAR_LENGTH);
} else {
#total size unknown
printf STDERR "\r\rThroughput: %9s/sec, total transferred: %d (%9s) ",
humanize_bytes($bytes/($now-$time)),
$totalbytes,
humanize_bytes($totalbytes);
}
$time = $now;
$bytes = 0;
}
my $written = 0;
while ($written != $ret) {
$written += syswrite(STDOUT,$buf,$ret-$written,$written);
}
}
total();
sub total {
my $now = time();
printf STDERR "\nBytes: %s (%s/sec) in %.02f seconds\n",
humanize_bytes($totalbytes),
humanize_bytes($totalbytes/($now-$start)),
($now - $start);
exit;
}
# quick and dirty local version to display understandable sizes
# for mere humans than cannot read sizes in bytes
sub humanize_bytes {
my $len = int(shift);
my $r;
if ($len >= 2 ** 30){
$r = sprintf("%.1f GiB", $len / 2 ** 30);
} elsif ($len >= 2 ** 20){
$r = sprintf("%.1f MiB", $len / 2 ** 20);
} elsif ($len >= 2 ** 10){
$r = sprintf("%.1f KiB", $len / 2 ** 10);
} else {
$r = $len;
}
return $r;
}
sub progress_bar {
my ($progression, $total, $width) = @_;
my $pchars = $width / $total * $progression;
my $pct = 100 / $total * $progression;
my $bar = sprintf("%s%s %.1f%%",
'#' x int($pchars),
'.' x ($width - int($pchars)),
$pct);
return $bar;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment