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', @_);
19 'C' => sub { $opt{color} = 0 },
21 'trim|length|l=s' => sub {
22 my ($optname, $optval) = @_;
23 $optval =~ s/%$// and $opt{trimpct}++;
24 $optval =~ m/^-?[0-9]+$/ or die(
25 "Value \"$optval\" invalid for option $optname",
26 " (number or percentage expected)\n"
33 'usage|h' => sub { podexit() },
34 'help' => sub { podexit(-verbose => 2) },
35 ) or exit 64; # EX_USAGE
37 $opt{width} ||= $ENV{COLUMNS} || 80;
38 $opt{color} //= -t *STDOUT; # enable on tty
39 $opt{trim} *= $opt{width} / 100 if $opt{trimpct};
41 if (defined $opt{interval}) {
50 $SIG{INT} = 'IGNORE'; # continue after assumed eof
55 s/^\h*// unless $opt{unmodified};
56 push @values, s/^ ( \h* -? [0-9]* \.? [0-9]+ |)//x && $1;
57 if (defined $opt{trim}) {
58 my $trimpos = abs $opt{trim};
62 elsif (length > $trimpos) {
63 substr($_, $trimpos - 1) = '…';
69 $SIG{INT} = 'DEFAULT';
74 @lines and @lines > $nr or return;
76 my @order = sort { $b <=> $a } grep { length } @values;
77 my $maxval = $order[0];
78 my $minval = min $order[-1], 0;
79 my $lenval = max map { length } @order;
80 my $len = defined $opt{trim} && $opt{trim} <= 0 ? -$opt{trim} + 1 :
81 1 + max map { length $values[$_] && length $lines[$_] } 0 .. $#lines; # left padding
82 my $size = ($maxval - $minval) &&
83 ($opt{width} - $lenval - $len) / ($maxval - $minval); # bar multiplication
86 if ($opt{markers} // 1 and $size > 0) {
87 my sub orderpos { (($order[$_[0]] + $order[$_[0] + .5]) / 2 - $minval) * $size }
88 $barmark[ (sum(@order) / @order - $minval) * $size ] = '='; # average
89 $barmark[ orderpos($#order * .31731) ] = '>';
90 $barmark[ orderpos($#order * .68269) ] = '<';
91 $barmark[ orderpos($#order / 2) ] = '+'; # mean
92 $barmark[ -$minval * $size ] = '|' if $minval < 0; # zero
93 defined and $opt{color} and $_ = "\e[36m$_\e[0m" for @barmark;
95 state $lastmax = $maxval;
96 if ($maxval > $lastmax) {
97 print ' ' x ($lenval + $len);
98 printf "\e[90m" if $opt{color};
100 ($lastmax - $minval) * $size + .5,
101 '-' x (($values[$nr - 1] - $minval) * $size);
102 print "\e[92m" if $opt{color};
103 say '+' x (($maxval - $lastmax - $minval) * $size + .5);
104 print "\e[0m" if $opt{color};
109 while ($nr <= $#lines) {
110 my $val = $values[$nr];
112 my $color = !$opt{color} ? 0 :
113 $val == $order[0] ? 32 : # max
114 $val == $order[-1] ? 31 : # min
116 printf "\e[%sm", $color if $color;
117 printf "%*s", $lenval, $val;
118 print "\e[0m" if $color;
120 printf '%-*s', $len, $lines[$nr];
121 print $barmark[$_] // '-' for 1 .. $size && (($val || 0) - $minval) * $size;
133 graph - append bar chart to input numbers
137 B<graph> [<options>] [<input>]
141 Each line starting with a number is given a bar to visualise relative sizes.
147 =item -c, --[no-]color
149 Force colored output of values and bar markers.
150 Defaults on if output is a tty,
151 disabled otherwise such as when piped or redirected.
153 =item -t, --interval[=<seconds>]
155 Interval time to output partial progress.
157 =item -l, --length=[-]<size>[%]
159 Trim line contents (between number and bars)
160 to a maximum number of characters.
161 The exceeding part is replaced by an abbreviation sign,
162 unless C<--length=0>.
164 Prepend a dash (i.e. make negative) to enforce padding
165 regardless of encountered contents.
169 Statistical positions to indicate on bars.
170 Cannot be customized yet,
171 only disabled by providing an empty argument.
173 Any value enables all marker characters:
180 the sum of all values divided by the number of counted lines.
185 the middle value or average between middle values.
189 Standard deviation left of the mean.
190 Only 16% of all values are lower.
194 Standard deviation right of the mean.
195 The part between B<< <--> >> encompass all I<normal> results,
196 or 68% of all entries.
200 =item -u, --unmodified
202 Do not strip leading whitespace.
203 Keep original value alignment, which may be significant in some programs.
205 =item -w, --width=<columns>
207 Override the maximum number of columns to use.
208 Appended graphics will extend to fill up the entire screen.
214 Commonly used after counting, such as users on the current server:
216 users | sed 's/ /\n/g' | sort | uniq -c | graph
218 Letter frequencies in text files:
220 cat /usr/share/games/fortunes/*.u8 |
221 perl -CO -nE 'say for grep length, split /\PL*/, uc' |
222 sort | uniq -c | graph
224 Memory usage of user processes:
226 ps xo %mem,pid,cmd | graph -l40
228 Sizes (in megabytes) of all root files and directories:
232 Number of HTTP requests per day:
234 cat log/access.log | cut -d\ -f4 | cut -d: -f1 | uniq -c | graph
236 Any kind of database query with leading counts:
238 echo 'SELECT count(*),schemaname FROM pg_tables GROUP BY 2' |
241 Exchange rate USD/EUR history from CSV download provided by ECB:
243 curl https://sdw.ecb.europa.eu/export.do \
244 -Gd 'node=SEARCHRESULTS&q=EXR.D.USD.EUR.SP00.A&exportType=csv' |
245 awk -F, '{RS="\r\n"} /^[12]/{print $2,$1}' | graph
247 Total population history from the World Bank dataset (XML):
249 curl http://api.worldbank.org/v2/country/1W/indicator/SP.POP.TOTL |
250 xmllint --xpath '//*[local-name()="date" or local-name()="value"]' - |
251 awk -F'<[^>]+>' 'BEGIN {RS="</wb:value>"} {print $4,$2}' | graph
253 Movies per year from prepared JSON data:
255 curl https://github.com/prust/wikipedia-movie-data/raw/master/movies.json |
256 jq '.[].year' | uniq -c | graph
258 Pokémon height comparison:
260 curl https://github.com/Biuni/PokemonGO-Pokedex/raw/master/pokedex.json |
261 jq -r '.pokemon[] | [.height,.num,.name] | join(" ")' | graph
263 Git statistics, such commit count by year:
265 git log --pretty=%ci | cut -b-4 | uniq -c | graph
267 Or the most frequent authors:
269 git shortlog -sn | graph | head -3
274 perl -pe '$|=1; print s/ time=(.*)// ? "$1 for " : "> "' | graph -t
278 Mischa POSLAWSKY <perl@shiar.org>