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', @_);
23 'usage|h' => sub { podexit() },
24 'help' => sub { podexit(-verbose => 2) },
25 ) or exit 64; # EX_USAGE
27 $opt{width} ||= $ENV{COLUMNS} || 80;
28 $opt{color} //= -t *STDOUT; # enable on tty
30 if (defined $opt{follow}) {
39 $SIG{INT} = 'IGNORE'; # continue after assumed eof
44 s/^\h*// unless $opt{unmodified};
45 push @values, s/^ ( \h* -? [0-9]* \.? [0-9]+ |)//x && $1;
46 if (defined $opt{trim}) {
47 my $trimpos = abs $opt{trim};
51 elsif (length > $trimpos) {
52 substr($_, $trimpos - 1) = '…';
58 $SIG{INT} = 'DEFAULT';
63 @lines and @lines > $nr or return;
65 my @order = sort { $b <=> $a } grep { length } @values;
66 my $maxval = $order[0];
67 my $minval = min $order[-1], 0;
68 my $lenval = max map { length } @order;
69 my $len = defined $opt{trim} && $opt{trim} <= 0 ? -$opt{trim} + 1 :
70 1 + max map { length } @lines; # left padding
71 my $size = ($maxval - $minval) &&
72 ($opt{width} - $lenval - $len) / ($maxval - $minval); # bar multiplication
75 if ($opt{markers} // 1 and $size > 0) {
76 my sub orderpos { (($order[$_[0]] + $order[$_[0] + .5]) / 2 - $minval) * $size }
77 $barmark[ (sum(@order) / @order - $minval) * $size ] = '='; # average
78 $barmark[ orderpos($#order * .31731) ] = '>';
79 $barmark[ orderpos($#order * .68269) ] = '<';
80 $barmark[ orderpos($#order / 2) ] = '+'; # mean
81 $barmark[ -$minval * $size ] = '|' if $minval < 0; # zero
82 defined and $opt{color} and $_ = "\e[36m$_\e[0m" for @barmark;
84 state $lastmax = $maxval;
85 if ($maxval > $lastmax) {
86 print ' ' x ($lenval + $len);
87 printf "\e[90m" if $opt{color};
89 ($lastmax - $minval) * $size + .5,
90 '-' x (($values[$nr - 1] - $minval) * $size);
91 print "\e[92m" if $opt{color};
92 say '+' x (($maxval - $lastmax - $minval) * $size + .5);
93 print "\e[0m" if $opt{color};
98 while ($nr <= $#lines) {
99 my $val = $values[$nr];
101 my $color = !$opt{color} ? 0 :
102 $val == $order[0] ? 32 : # max
103 $val == $order[-1] ? 31 : # min
105 printf "\e[%sm", $color if $color;
106 printf "%*s", $lenval, $val;
107 print "\e[0m" if $color;
109 printf '%-*s', $len, $lines[$nr];
110 print $barmark[$_] // '-' for 1 .. $size && (($val || 0) - $minval) * $size;
122 graph - append bar chart to input numbers
126 B<graph> [<options>] [<input>]
130 Each line starting with a number is given a bar to visualise relative sizes.
136 =item -c, --[no-]color
138 Force colored output of values and bar markers.
139 Defaults on if output is a tty,
140 disabled otherwise such as when piped or redirected.
142 =item -f, --follow[=<seconds>]
144 Interval to output partial progress.
146 =item -l, --length=[-]<size>
148 Trim line contents (between number and bars)
149 to a maximum number of characters.
150 The exceeding part is replaced by an abbreviation sign,
151 unless C<--length=0>.
153 Prepend a dash (i.e. make negative) to enforce padding
154 regardless of encountered contents.
158 Statistical positions to indicate on bars.
159 Cannot be customized yet,
160 only disabled by providing an empty argument.
162 Any value enables all marker characters:
169 the sum of all values divided by the number of counted lines.
174 the middle value or average between middle values.
178 Standard deviation left of the mean.
179 Only 16% of all values are lower.
183 Standard deviation right of the mean.
184 The part between B<< <--> >> encompass all I<normal> results,
185 or 68% of all entries.
189 =item -u, --unmodified
191 Do not strip leading whitespace.
192 Keep original value alignment, which may be significant in some programs.
194 =item -w, --width=<columns>
196 Override the maximum number of columns to use.
197 Appended graphics will extend to fill up the entire screen.
203 Commonly used after counting, such as users on the current server:
205 users | sed 's/ /\n/g' | sort | uniq -c | graph
207 Letter frequencies in text files:
209 cat /usr/share/games/fortunes/*.u8 |
210 perl -CO -nE 'say for grep length, split /\PL*/, uc' |
211 sort | uniq -c | graph
213 Memory usage of user processes:
215 ps xo %mem,pid,cmd | graph -l40
217 Sizes (in megabytes) of all root files and directories:
221 Number of HTTP requests per day:
223 cat log/access.log | cut -d\ -f4 | cut -d: -f1 | uniq -c | graph
225 Any kind of database query with leading counts:
227 echo 'SELECT count(*),schemaname FROM pg_tables GROUP BY 2' |
230 Git statistics, such commit count by year:
232 git log --pretty=%ci | cut -b-4 | uniq -c | graph
234 Or the most frequent authors:
236 git shortlog -sn | graph | head -3
241 perl -pe '$|=1; print s/ time=(.*)// ? "$1 for " : "> "' | graph -f
245 Mischa POSLAWSKY <perl@shiar.org>