5 use List::Util qw( min max sum );
6 use open qw( :std :utf8 );
10 use Getopt::Long '2.33', qw( :config gnu_getopt );
11 sub podexit { require Pod::Usage; Pod::Usage::pod2usage(-exitval => 0, @_) }
18 'usage|h' => sub { podexit() },
19 'help' => sub { podexit(-verbose => 2) },
20 ) or exit 64; # EX_USAGE
21 $opt{width} ||= $ENV{COLUMNS} || 80;
24 if (defined $opt{follow}) {
36 push @values, s/^\h* ( -? [0-9]* (?:\.[0-9]+)? )//x && $1;
37 if (defined $opt{trim}) {
38 my $trimpos = abs $opt{trim};
42 elsif (length > $trimpos) {
43 substr($_, $trimpos - 1) = '…';
52 my @order = sort { $b <=> $a } grep { length } @values;
53 my $maxval = $order[0];
54 my $minval = min $order[-1], 0;
55 my $lenval = max map { length } @order;
56 my $len = defined $opt{trim} && $opt{trim} <= 0 ? -$opt{trim} + 1 :
57 1 + max map { length } @lines; # left padding
58 my $size = ($maxval - $minval) &&
59 ($opt{width} - $lenval - $len) / ($maxval - $minval); # bar multiplication
62 if ($opt{markers} // 1 and $size > 0) {
63 my sub orderpos { (($order[$_[0]] + $order[$_[0] + .5]) / 2 - $minval) * $size }
64 $barmark[ (sum(@order) / @order - $minval) * $size ] = '='; # average
65 $barmark[ orderpos($#order * .31731) ] = '>';
66 $barmark[ orderpos($#order * .68269) ] = '<';
67 $barmark[ orderpos($#order / 2) ] = '+'; # mean
68 $barmark[ -$minval * $size ] = '|' if $minval < 0; # zero
69 defined and $opt{color} and $_ = "\e[36m$_\e[0m" for @barmark;
73 while ($nr <= $#lines) {
74 my $val = $values[$nr];
76 my $color = !$opt{color} ? 0 :
77 $val == $order[0] ? 32 : # max
78 $val == $order[-1] ? 31 : # min
80 printf "\e[%sm", $color if $color;
81 printf "%*s", $lenval, $val;
82 print "\e[0m" if $color;
84 printf '%-*s', $len, $lines[$nr];
85 print $barmark[$_] // '-' for 1 .. $size && (($val || 0) - $minval) * $size;
97 graph - append bar chart to input numbers
101 B<graph> [<options>] [<input>]
105 Each line starting with a number is given a bar to visualise relative sizes.
113 Disable colored output of values and bar markers.
115 =item -f, --follow[=<seconds>]
117 Interval to output partial progress.
119 =item -l, --length=[-]<size>
121 Trim line contents (between number and bars)
122 to a maximum number of characters.
123 The exceeding part is replaced by an abbreviation sign,
124 unless C<--length=0>.
126 Prepend a dash (i.e. make negative) to enforce padding
127 regardless of encountered contents.
131 Statistical positions to indicate on bars.
132 Cannot be customized yet,
133 only disabled by providing an empty argument.
135 Any value enables all marker characters:
142 the sum of all values divided by the number of counted lines.
147 the middle value or average between middle values.
151 Standard deviation left of the mean.
152 Only 16% of all values are lower.
156 Standard deviation right of the mean.
157 The part between B<< <--> >> encompass all I<normal> results,
158 or 68% of all entries.
162 =item -w, --width=<columns>
164 Override the maximum number of columns to use.
165 Appended graphics will extend to fill up the entire screen.
171 Commonly used after counting, such as users on the current server:
173 users | sed 's/ /\n/g' | sort | uniq -c | graph
175 Letter frequencies in text files:
177 cat /usr/share/games/fortunes/*.u8 |
178 perl -CO -nE 'say for grep length, split /\PL*/, uc' |
179 sort | uniq -c | graph
181 Memory usage of user processes:
183 ps xo %mem,pid,cmd | graph -l40
185 Sizes (in megabytes) of all root files and directories:
189 Number of HTTP requests per day:
191 cat log/access.log | cut -d\ -f4 | cut -d: -f1 | uniq -c | graph
193 Any kind of database query with leading counts:
195 echo 'SELECT count(*),schemaname FROM pg_tables GROUP BY 2' |
198 Git statistics, such commit count by year:
200 git log --pretty=%ci | cut -b-4 | uniq -c | graph
202 Or the most frequent authors:
204 git shortlog -sn | graph | head -3
209 perl -pe '$|=1; print s/ time=(.*)// ? "$1 for " : "> "' | graph -f
213 Mischa POSLAWSKY <perl@shiar.org>