X-Git-Url: http://git.shiar.nl/barcat.git/blobdiff_plain/45f511dfa9c64725f7113a2a57a98435c3c1b09a..9550ed22b3f54bf1e0fb27eebee8099334fc9cb1:/barcat diff --git a/barcat b/barcat index 2a931c6..e898b6c 100755 --- a/barcat +++ b/barcat @@ -6,7 +6,7 @@ use List::Util qw( min max sum ); use open qw( :std :utf8 ); use re '/msx'; -our $VERSION = '1.08'; +our $VERSION = '1.09'; my %opt; if (@ARGV) { @@ -54,6 +54,7 @@ GetOptions(\%opt, " (range expected)\n" ); }, + 'log|e!', 'header!', 'markers|m=s', 'graph-format=s' => sub { @@ -89,6 +90,7 @@ GetOptions(\%opt, }; }, 'stat|s!', + 'report=s', 'signal-stat=s', 'unmodified|u!', 'width|w=i', @@ -114,13 +116,18 @@ $opt{width} ||= $ENV{COLUMNS} || qx(tput cols) || 80 unless $opt{spark}; $opt{color} //= $ENV{NO_COLOR} ? 0 : -t *STDOUT; # enable on tty $opt{'graph-format'} //= '-'; $opt{trim} *= $opt{width} / 100 if $opt{trimpct}; -$opt{units} = [split //, ' kMGTPEZYyzafpn'.($opt{ascii} ? 'u' : 'μ').'m'] +$opt{units} = [split //, ' kMGTPEZYRQqryzafpn'.($opt{ascii} ? 'u' : 'μ').'m'] if $opt{'human-readable'}; $opt{anchor} //= qr/\A/; $opt{'value-length'} = 4 if $opt{units}; $opt{'value-length'} = 1 if $opt{unmodified}; $opt{'signal-stat'} //= exists $SIG{INFO} ? 'INFO' : 'QUIT'; $opt{markers} //= '=avg >31.73v <68.27v +50v |0'; +$opt{report} //= join(', ', + '${min; color(31)} min', + '${avg; $opt{reformat} or $_ = sprintf "%0.2f", $_; color(36)} avg', + '${max; color(32)} max', +); $opt{palette} //= $opt{color} && [31, 90, 32]; $opt{indicators} = [split //, $opt{indicators} || ($opt{ascii} ? ' .oO' : $opt{spark} ? ' ▁▂▃▄▅▆▇█' : ' ▏▎▍▌▋▊▉█') @@ -230,12 +237,13 @@ my $maxval = $opt{maxval} // ( ) // 0; my $minval = $opt{minval} // min $order[-1] // (), 0; my $range = $maxval - $minval; +$range &&= log $range if $opt{log}; my $lenval = $opt{'value-length'} // max map { length } @order; my $len = defined $opt{trim} && $opt{trim} <= 0 ? -$opt{trim} + 1 : max map { length $values[$_] && length $lines[$_] } 0 .. min $#lines, $opt{hidemax} || (); # left padding my $size = defined $opt{width} && $range && - ($opt{width} - $lenval - $len - !!$opt{indicators}) / $range; # bar multiplication + ($opt{width} - $lenval - $len - !!$opt{indicators}); # bar multiplication my @barmark; if ($opt{markers} and $size > 0) { @@ -246,7 +254,9 @@ if ($opt{markers} and $size > 0) { return sum(@order) / @order; } elsif ($func =~ /\A([0-9.]+)v\z/) { - die "Invalid marker $char: percentile $1 out of bounds\n" if $1 > 100; + $1 <= 100 or die( + "Invalid marker $char: percentile $1 out of bounds\n" + ); my $index = $#order * $1 / 100; return ($order[$index] + $order[$index + .5]) / 2; } @@ -262,8 +272,9 @@ if ($opt{markers} and $size > 0) { next; }; $pos -= $minval; + $pos &&= log $pos if $opt{log}; $pos >= 0 or next; - color(36) for $barmark[$pos * $size] = $char; + color(36) for $barmark[$pos / $range * $size] = $char; } state $lastmax = $maxval; @@ -271,10 +282,10 @@ if ($opt{markers} and $size > 0) { print ' ' x ($lenval + $len); printf color(90); printf '%-*s', - ($lastmax - $minval) * $size + .5, - '-' x (($values[$nr - 1] - $minval) * $size); + ($lastmax - $minval) * $size / $range + .5, + '-' x (($values[$nr - 1] - $minval) * $size / $range); print color(92); - say '+' x (($range - $lastmax) * $size + .5); + say '+' x (($range - $lastmax) * $size / $range + .5); print color(0); $lastmax = $maxval; } @@ -283,14 +294,19 @@ if ($opt{markers} and $size > 0) { say( color(31), sprintf('%*s', $lenval, $minval), color(90), '-', color(36), '+', - color(32), sprintf('%*s', $size * $range - 3, $maxval), + color(32), sprintf('%*s', $size - 3, $maxval), color(90), '-', color(36), '+', color(0), ) if $opt{header}; while ($nr <= $limit) { my $val = $values[$nr]; - my $rel = length $val && $range && min(1, ($val - $minval) / $range); + my $rel; + if (length $val) { + $rel = $val - $minval; + $rel &&= log $rel if $opt{log}; + $rel = min(1, $rel / $range) if $range; # 0..1 + } my $color = !length $val || !$opt{palette} ? undef : $val == $order[0] ? $opt{palette}->[-1] : # max $val == $order[-1] ? $opt{palette}->[0] : # min @@ -321,8 +337,10 @@ while ($nr <= $limit) { next; } printf '%-*s', $len + length($val), $line; - print $barmark[$_] // $opt{'graph-format'} - for 1 .. $size && (($values[$nr] || 0) - $minval) * $size + .5; + if ($rel and $size) { + print $barmark[$_] // $opt{'graph-format'} + for 1 .. $rel * $size + .5; + } say ''; } continue { @@ -341,25 +359,51 @@ sub show_stat { $linemin += @lines; $linemax = @lines - $linemax; } - printf '%.8g of ', $opt{'value-format'}->( - sum(grep {length} @values[$linemin .. $linemax]) // 0 - ); + print varfmt('${sum+} of ', { + lines => $linemax - $linemin + 1, + sum => sum(0, grep {length} @values[$linemin .. $linemax]), + }); } if (@order) { my $total = sum @order; - printf '%s total', color(1) . $opt{'value-format'}->($total) . color(0); - printf ' in %d values', scalar @order; - printf ' over %d lines', scalar @lines if @order != @lines; - printf(' (%s min, %s avg, %s max)', - color(31) . ($opt{reformat} ? $opt{'value-format'} : sub {$_[0]})->($order[-1]) . color(0), - color(36) . ($opt{reformat} ? $opt{'value-format'} : $opt{'calc-format'})->($total / @order) . color(0), - color(32) . ($opt{reformat} ? $opt{'value-format'} : sub {$_[0]})->($order[0]) . color(0), - ); + my $fmt = '${sum+;color(1)} total in ${count#} values'; + $fmt .= ' over ${lines#} lines' if @order != @lines; + $fmt .= " ($_)" for $opt{report} || (); + print varfmt($fmt, { + sum => $total, + count => int @order, + lines => int @lines, + min => $order[-1], + max => $order[0], + avg => $total / @order, + }); } say ''; return 1; } +sub varfmt { + my ($fmt, $vars) = @_; + $fmt =~ s[\$\{ \h*+ ((?: [^{}]++ | \{(?1)\} )+) \}]{ + my ($name, $cmd) = split /\s*;/, $1, 2; + my $format = $name =~ s/\+// || $name !~ s/\#// && $opt{reformat}; + local $_ = $vars->{$name}; + if (defined) { + $_ = $opt{'value-format'}->($_) if $format; + if ($cmd) { + eval $cmd; + warn "Error in \$$name report: $@" if $@; + } + $_; + } + else { + warn "Unknown variable \$$name in report\n"; + "\$$name"; + } + }eg; + return $fmt; +} + sub show_exit { show_lines(); show_stat() if $opt{stat}; @@ -389,6 +433,7 @@ Options: -l, --length=[-]SIZE[%] Trim line contents (between number and bars) -L, --limit[=(N|-LAST|START-[END])] Stop output after a number of lines + -e, --log Logarithmic (exponential) scale instead of linear --graph-format=CHAR Glyph to repeat for the graph line -m, --markers=FORMAT Statistical positions to indicate on bars --min=N, --max=N Bars extend from 0 or the minimum value if lower @@ -499,6 +544,11 @@ A specific range can be given by two values. All input is still counted and analyzed for statistics, but disregarded for padding and bar size. +=item -e, --log + +Logarithmic (Ixponential) scale instead of linear +to compare orders of magnitude. + =item --graph-format= Glyph to repeat for the graph line. @@ -650,14 +700,21 @@ Number of HTTP requests per day: cat httpd/access.log | cut -d\ -f4 | cut -d: -f1 | uniq -c | barcat -Any kind of database query with counts, preserving returned alignment: +Any kind of database query results, preserving returned alignment: - echo 'SELECT count(*),schemaname FROM pg_tables GROUP BY 2' | + echo 'SELECT sin(value * .1) FROM generate_series(0, 30) value' | psql -t | barcat -u -In PostgreSQL from within the client: +In PostgreSQL from within the client; a fancy C<\dt+> perhaps: + + > SELECT schemaname, relname, pg_total_relation_size(relid) + FROM pg_statio_user_tables ORDER BY idx_blks_hit + \g |barcat -uHf+ + +Same thing in SQLite (requires the sqlite3 client): - > SELECT sin(generate_series(0, 3, .1)) \g |barcat + > .once |barcat -Hf+ + > SELECT name, sum(pgsize) FROM dbstat GROUP BY 1; Earthquakes worldwide magnitude 1+ in the last 24 hours: @@ -689,7 +746,7 @@ Total population history in XML from the World Bank: Population and other information for all countries: curl http://download.geonames.org/export/dump/countryInfo.txt | - grep -v '^#\s' | column -ts$'\t' -n | barcat -f+2 -u -l150 -s + grep -v '^#\s' | column -ts$'\t' -n | barcat -f+2 -e -u -l150 -s And of course various Git statistics, such commit count by year: