5 use List::Util qw( min max sum );
6 use open qw( :std :utf8 );
7 use experimental qw( lexical_subs );
11 use Getopt::Long '2.33', qw( :config gnu_getopt );
14 Pod::Usage::pod2usage(-exitval => 0, -perldocopt => '-oman', @_);
20 'trim|length|l=s' => sub {
21 my ($optname, $optval) = @_;
22 $optval =~ s/%$// and $opt{trimpct}++;
23 $optval =~ m/^-?[0-9]+$/ or die(
24 "Value \"$optval\" invalid for option $optname",
25 " (number or percentage expected)\n"
32 'usage|h' => sub { podexit() },
33 'help' => sub { podexit(-verbose => 2) },
34 ) or exit 64; # EX_USAGE
36 $opt{width} ||= $ENV{COLUMNS} || 80;
37 $opt{color} //= -t *STDOUT; # enable on tty
38 $opt{trim} *= $opt{width} / 100 if $opt{trimpct};
40 if (defined $opt{follow}) {
49 $SIG{INT} = 'IGNORE'; # continue after assumed eof
54 s/^\h*// unless $opt{unmodified};
55 push @values, s/^ ( \h* -? [0-9]* \.? [0-9]+ |)//x && $1;
56 if (defined $opt{trim}) {
57 my $trimpos = abs $opt{trim};
61 elsif (length > $trimpos) {
62 substr($_, $trimpos - 1) = '…';
68 $SIG{INT} = 'DEFAULT';
73 @lines and @lines > $nr or return;
75 my @order = sort { $b <=> $a } grep { length } @values;
76 my $maxval = $order[0];
77 my $minval = min $order[-1], 0;
78 my $lenval = max map { length } @order;
79 my $len = defined $opt{trim} && $opt{trim} <= 0 ? -$opt{trim} + 1 :
80 1 + max map { length } @lines; # left padding
81 my $size = ($maxval - $minval) &&
82 ($opt{width} - $lenval - $len) / ($maxval - $minval); # bar multiplication
85 if ($opt{markers} // 1 and $size > 0) {
86 my sub orderpos { (($order[$_[0]] + $order[$_[0] + .5]) / 2 - $minval) * $size }
87 $barmark[ (sum(@order) / @order - $minval) * $size ] = '='; # average
88 $barmark[ orderpos($#order * .31731) ] = '>';
89 $barmark[ orderpos($#order * .68269) ] = '<';
90 $barmark[ orderpos($#order / 2) ] = '+'; # mean
91 $barmark[ -$minval * $size ] = '|' if $minval < 0; # zero
92 defined and $opt{color} and $_ = "\e[36m$_\e[0m" for @barmark;
94 state $lastmax = $maxval;
95 if ($maxval > $lastmax) {
96 print ' ' x ($lenval + $len);
97 printf "\e[90m" if $opt{color};
99 ($lastmax - $minval) * $size + .5,
100 '-' x (($values[$nr - 1] - $minval) * $size);
101 print "\e[92m" if $opt{color};
102 say '+' x (($maxval - $lastmax - $minval) * $size + .5);
103 print "\e[0m" if $opt{color};
108 while ($nr <= $#lines) {
109 my $val = $values[$nr];
111 my $color = !$opt{color} ? 0 :
112 $val == $order[0] ? 32 : # max
113 $val == $order[-1] ? 31 : # min
115 printf "\e[%sm", $color if $color;
116 printf "%*s", $lenval, $val;
117 print "\e[0m" if $color;
119 printf '%-*s', $len, $lines[$nr];
120 print $barmark[$_] // '-' for 1 .. $size && (($val || 0) - $minval) * $size;
132 graph - append bar chart to input numbers
136 B<graph> [<options>] [<input>]
140 Each line starting with a number is given a bar to visualise relative sizes.
146 =item -c, --[no-]color
148 Force colored output of values and bar markers.
149 Defaults on if output is a tty,
150 disabled otherwise such as when piped or redirected.
152 =item -f, --follow[=<seconds>]
154 Interval to output partial progress.
156 =item -l, --length=[-]<size>[%]
158 Trim line contents (between number and bars)
159 to a maximum number of characters.
160 The exceeding part is replaced by an abbreviation sign,
161 unless C<--length=0>.
163 Prepend a dash (i.e. make negative) to enforce padding
164 regardless of encountered contents.
168 Statistical positions to indicate on bars.
169 Cannot be customized yet,
170 only disabled by providing an empty argument.
172 Any value enables all marker characters:
179 the sum of all values divided by the number of counted lines.
184 the middle value or average between middle values.
188 Standard deviation left of the mean.
189 Only 16% of all values are lower.
193 Standard deviation right of the mean.
194 The part between B<< <--> >> encompass all I<normal> results,
195 or 68% of all entries.
199 =item -u, --unmodified
201 Do not strip leading whitespace.
202 Keep original value alignment, which may be significant in some programs.
204 =item -w, --width=<columns>
206 Override the maximum number of columns to use.
207 Appended graphics will extend to fill up the entire screen.
213 Commonly used after counting, such as users on the current server:
215 users | sed 's/ /\n/g' | sort | uniq -c | graph
217 Letter frequencies in text files:
219 cat /usr/share/games/fortunes/*.u8 |
220 perl -CO -nE 'say for grep length, split /\PL*/, uc' |
221 sort | uniq -c | graph
223 Memory usage of user processes:
225 ps xo %mem,pid,cmd | graph -l40
227 Sizes (in megabytes) of all root files and directories:
231 Number of HTTP requests per day:
233 cat log/access.log | cut -d\ -f4 | cut -d: -f1 | uniq -c | graph
235 Any kind of database query with leading counts:
237 echo 'SELECT count(*),schemaname FROM pg_tables GROUP BY 2' |
240 Git statistics, such commit count by year:
242 git log --pretty=%ci | cut -b-4 | uniq -c | graph
244 Or the most frequent authors:
246 git shortlog -sn | graph | head -3
251 perl -pe '$|=1; print s/ time=(.*)// ? "$1 for " : "> "' | graph -f
255 Mischa POSLAWSKY <perl@shiar.org>