5 use List::Util qw( min max sum );
6 use open qw( :std :utf8 );
14 Getopt::Long->import('2.33', qw( :config gnu_getopt ));
18 'M' => sub { $opt{color} = 0 },
22 s/\A[0-9]+\z/(?:\\S*\\h+){$_}\\K/;
24 (!!$1 && '(?:\d+\D+\b){'.$1.'}\K') . '\s* (?=\d)'
26 $opt{anchor} = qr/$_/;
27 } or die $@ =~ s/(?:\ at\ \N+)?\Z/ for option $_[0]/r;
34 'trim|length|l=s' => sub {
35 my ($optname, $optval) = @_;
36 $optval =~ s/%$// and $opt{trimpct}++;
37 $optval =~ m/\A-?[0-9]+\z/ or die(
38 "Value \"$optval\" invalid for option $optname",
39 " (number or percentage expected)\n"
47 my ($optname, $optval) = @_;
49 $optval =~ /\A-[0-9]+\z/ and $optval .= '-'; # tail shorthand
52 $optval =~ m/\A (?: (-? [0-9]+)? - )? (-? [0-9]+)? \z/ or die(
53 "Value \"$optval\" invalid for option limit",
58 s/\A-0*\z// and $_ ||= undef for $end // ();
63 return max(0, $lines + $start + 2);
68 my ($limit, $offset) = @_;
70 return $offset - $end - 1; # count
73 return $limit - $end + 1; # bottom
75 elsif ($end <= $limit) {
76 return $end - 1; # less
84 'graph-format=s' => sub {
85 $opt{'graph-format'} = substr $_[1], 0, 1;
92 fire => [qw( 90 31 91 33 93 97 96 )],
93 fire256=> [map {"38;5;$_"} qw(
95 202 208 214 220 226 227 228 229 230 231 159
97 whites => [qw( 1;30 0;37 1;37 )],
98 grays => [map {"38;5;$_"} 0, 232..255, 15],
99 random => [map {"38;5;$_"} List::Util::shuffle(17..231)],
100 rainbow=> [map {"38;5;$_"}
102 (map { 196 + $_*6 } 0..4), # +g
103 (map { 226 - $_*6*6 } 0..4), # -r
104 (map { 46 + $_ } 0..4), # +b
105 (map { 51 - $_*6 } 0..4), # -g
106 (map { 21 + $_*6*6 } 0..4), # +r
107 (map { 201 - $_ } 0..4), # -b
111 my @vals = split /[^0-9;]/, $_[1]
112 or die "Empty palette resulting from \"$_[1]\"\n";
122 my $mascot = $opt{ascii} ? '=^,^=' : 'ฅ^•ﻌ•^ฅ';
123 say "barcat $mascot version $VERSION";
127 /^=/ ? last : print for readline *DATA; # text between __END__ and pod
132 Pod::Usage::pod2usage(
133 -exitval => 0, -perldocopt => '-oman', -verbose => 2,
136 ) or exit 64; # EX_USAGE
139 $opt{width} ||= $ENV{COLUMNS} || qx(tput cols) || 80 unless $opt{spark};
140 $opt{color} //= $ENV{NO_COLOR} ? 0 : -t *STDOUT; # enable on tty
141 $opt{'graph-format'} //= '-';
142 $opt{trim} *= $opt{width} / 100 if $opt{trimpct};
143 $opt{units} = [split //, ' kMGTPEZYRQqryzafpn'.($opt{ascii} ? 'u' : 'μ').'m']
144 if $opt{'human-readable'};
145 $opt{'value-length'} = 4 if $opt{units};
146 $opt{'value-length'} = 1 if $opt{unmodified};
147 $opt{'signal-stat'} //= exists $SIG{INFO} ? 'INFO' : 'QUIT';
148 $opt{markers} //= '=avg >31.73v <68.27v +50v |0';
149 $opt{report} //= join('',
150 '${partsum+; $_ .= " of "}',
151 '${sum+; color(1); $_ .= " total in "}',
153 '${lines#; $_ = $_ != @order && " over $_ lines"}',
154 sprintf('${count: (%s)}', join ', ',
155 '${min; color(31)} min',
156 '${avg; $opt{reformat} or $_ = sprintf "%0.2f", $_; color(36)} avg',
157 '${max; color(32)} max',
160 $opt{palette} //= $opt{color} && [31, 90, 32];
161 $opt{indicators} = [split //, $opt{indicators} ||
162 ($opt{ascii} ? ' .oO' : $opt{spark} ? ' ▁▂▃▄▅▆▇█' : ' ▏▎▍▌▋▊▉█')
163 ] if defined $opt{indicators} or $opt{spark};
164 $opt{input} = (@ARGV && $ARGV[0] =~ m/\A[-0-9]/) ? \@ARGV : undef
165 and undef $opt{interval};
167 $opt{'calc-format'} = sub { sprintf '%*.*f', 0, 2, $_[0] };
168 $opt{'value-format'} = $opt{sexagesimal} ? sub {
169 my $s = abs($_[0]) + .5;
170 sprintf('%s%d:%02d:%02d', $_[0] < 0 && '-', $s/3600, $s/60%60, $s%60);
171 } : $opt{units} && sub {
173 log(abs $_[0] || 1) / log(10)
174 - 3 * (abs($_[0]) < .9995) # shift to smaller unit if below 1
175 + 1e-15 # float imprecision
177 my $decimal = ($unit % 3) == ($unit < 0);
178 $unit -= log($decimal ? .995 : .9995) / log(10); # rounded
179 $decimal = ($unit % 3) == ($unit < 0);
180 $decimal &&= $_[0] !~ /^-?0*[0-9]{1,3}$/; # integer 0..999
182 3 + ($_[0] < 0), # digits plus optional negative sign
184 $_[0] / 1000 ** int($unit/3), # number
185 $#{$opt{units}} * 1.5 < abs $unit ? sprintf('e%d', $unit) :
186 $opt{units}->[$unit/3] # suffix
188 } and $opt{reformat}++;
189 $opt{'value-format'} ||= sub { sprintf '%.8g', $_[0] };
192 my (@lines, @values, @order, %uniq);
194 $SIG{$_} = \&show_stat for $opt{'signal-stat'} || ();
197 alarm $opt{interval} if defined $opt{interval} and $opt{interval} > 0;
199 $SIG{INT} = \&show_exit;
201 if (defined $opt{interval}) {
202 $opt{interval} ||= 1;
203 alarm $opt{interval} if $opt{interval} > 0;
206 require Tie::Array::Sorted;
207 tie @order, 'Tie::Array::Sorted', sub { $_[1] <=> $_[0] };
208 } or warn $@, "Expect slowdown with large datasets!\n"
212 my $float = qr<[0-9]* [.]? [0-9]+ (?: e[+-]?[0-9]+ )?>; # positive numberish
213 my $valmatch = $opt{anchor} // qr/\A/;
214 $valmatch .= !$opt{count} ? qr/( \h* -? $float |)/ :
215 $opt{anchor} ? qr/(\S*)/ : qr/(.*)/;
217 while (defined ($_ = $opt{input} ? shift @{ $opt{input} } : readline)) {
221 $valnum = m/$valmatch/ && $1;
222 $uniq{$valnum}++ and next;
223 push @values, $valnum;
227 s/\A\h*// unless $opt{unmodified};
228 $valnum = s/$valmatch/\n/ && $1;
229 push @values, $valnum;
230 push @order, $valnum if length $valnum;
233 if (defined $opt{trim} and defined $valnum) {
234 my $trimpos = abs $opt{trim};
235 $trimpos -= length $valnum if $opt{unmodified};
237 $_ = substr $_, 0, 2;
239 elsif (length > $trimpos) {
240 # cut and replace (intentional lvalue for speed, contrary to PBP)
241 substr($_, $trimpos - 1) = $opt{ascii} ? '>' : '…';
247 show_lines() if defined $opt{interval} and $opt{interval} < 0
248 and $. % $opt{interval} == 0;
251 $SIG{INT} = 'DEFAULT';
254 $opt{color} and defined $_[0] or return '';
255 return "\e[$_[0]m" if defined wantarray;
256 $_ = color(@_) . $_ . color(0) if defined;
261 state $nr = $opt{hidemin} ? $opt{hidemin}->($#lines) : 0;
262 @lines > $nr or return;
264 my $limit = $opt{hidemax} ? $opt{hidemax}->($#lines, $nr) : $#lines;
267 $_ = $uniq{$_} for @values[$nr .. $limit];
271 @order = sort { $b <=> $a } @order unless tied @order;
272 my $maxval = $opt{maxval} // (
273 $opt{hidemax} ? max grep { length } @values[$nr .. $limit] :
276 my $minval = $opt{minval} // min $order[-1] // (), 0;
277 my $range = $maxval - $minval;
278 $range &&= log $range if $opt{log};
279 my $lenval = $opt{'value-length'} // max map { length } @order;
280 my $len = defined $opt{trim} && $opt{trim} <= 0 ? -$opt{trim} + 1 :
281 max(map { length $values[$_] && length $lines[$_] } $nr .. $limit)
283 my $size = defined $opt{width} && $range &&
284 ($opt{width} - $lenval - $len - !!$opt{indicators}); # bar multiplication
287 if ($opt{markers} and $size > 0) {
288 for my $markspec (split /\h/, $opt{markers}) {
289 my ($char, $func) = split //, $markspec, 2;
290 my $increment = $func =~ s/[+]\z//;
292 if ($func =~ /\A\/($float)\z/) {
293 my @range = my $multiple = my $next = $1;
294 while ($next < $maxval) {
295 $multiple *= 10 if $opt{log};
296 push @range, $next += $multiple;
302 warn "Invalid marker $char: $@" if $@;
307 $pos &&= log $pos if $opt{log};
309 $increment ||= $minval && !$pos;
310 color(36) for $barmark[$pos / $range * $size + $increment + .5] = $char;
314 state $lastmax = $maxval;
315 if ($maxval > $lastmax) {
316 print ' ' x ($lenval + $len);
319 ($lastmax - $minval) * $size / $range + .5,
320 '-' x (($values[$nr - 1] - $minval) * $size / $range);
322 say '+' x (($range - $lastmax) * $size / $range + .5);
329 color(31), sprintf('%*s', $lenval, $minval),
330 color(90), '-', color(36), '+',
331 color(32), sprintf('%*s', $size - 3, $maxval),
332 color(90), '-', color(36), '+',
336 while ($nr <= $limit) {
337 my $val = $values[$nr];
340 $rel = $val - $minval;
341 $rel &&= log $rel if $opt{log};
342 $rel = min(1, $rel / $range) if $range; # 0..1
344 my $color = !length $val || !$opt{palette} ? undef :
345 $val == $order[0] ? $opt{palette}->[-1] : # max
346 $val == $order[-1] ? $opt{palette}->[0] : # min
347 $opt{palette}->[ $rel * ($#{$opt{palette}} - 1) + 1 ];
348 my $indicator = $opt{indicators} && $opt{indicators}->[
349 !length($val) || !$#{$opt{indicators}} ? 0 : # blank
350 $#{$opt{indicators}} < 2 ? 1 :
351 $val >= $order[0] ? -1 :
352 $rel * ($#{$opt{indicators}} - 1e-14) + 1
356 say '' if $opt{width} and $nr and $nr % $opt{width} == 0;
357 print color($color), $_ for $indicator;
360 print $indicator if defined $indicator;
363 $val = sprintf("%*s", $lenval,
364 $opt{reformat} ? $opt{'value-format'}->($val) : $val
366 color($color) for $val;
368 my $line = $lines[$nr] =~ s/\n/$val/r;
369 if (not length $val) {
373 printf '%-*s', $len + length($val), $line;
374 if ($rel and $size) {
375 print $barmark[$_] // $opt{'graph-format'}
376 for 1 .. $rel * $size + .5;
383 say $opt{palette} ? color(0) : '' if $opt{spark};
384 %uniq = () if $opt{interval} and $opt{count};
395 my $linemin = !$opt{hidemin} ? 0 :
396 ($vars{start} = $opt{hidemin}->($#lines));
397 my $linemax = !$opt{hidemax} ? $#lines :
398 ($vars{end} = $opt{hidemax}->($#lines, $vars{start}));
400 $vars{partsum} = sum(0, grep {length} @values[$linemin .. $linemax])
401 if $linemin <= $linemax and ($opt{hidemin} or $opt{hidemax});
407 $vars{avg} = $vars{sum} / @order;
409 say varfmt($opt{report}, \%vars);
415 if ($func eq 'avg') {
416 return sum(@order) / @order;
418 elsif ($func eq 'sum') {
421 elsif ($func =~ /\A([0-9.]+)v\z/) {
423 "percentile $1 out of bounds\n"
425 my $index = $#order * $1 / 100;
426 my $f = $index - int $index;
427 my $val = $order[$index];
429 my $next = $order[$index + 1];
430 $val -= $f * ($val - $next);
434 elsif ($func =~ /\A-?[0-9.]+\z/) {
438 die "$func unknown\n";
443 my ($fmt, $vars) = @_;
444 $fmt =~ s[\$\{ \h*+ ((?: [^{}]++ | \{(?1)\} )+) \}]{
445 my ($name, $op, $cmd) = split /\s*([;:])/, $1, 2;
446 my $format = $name =~ s/\+// || $name !~ s/\#// && $opt{reformat};
447 local $_ = exists $vars->{$name} ? $vars->{$name} : calc($name);
449 $_ = $opt{'value-format'}->($_) if $format;
450 if ($cmd and $op eq ':') {
451 $_ = !!$_ && varfmt($cmd, $vars);
455 warn "Error in \$$name report: $@" if $@;
465 show_stat() if $opt{stat};
466 exit 130 if @_; # 0x80+signo
474 barcat [OPTIONS] [FILES|NUMBERS] (=•.•=)
477 -a, --[no-]ascii Restrict user interface to ASCII characters
478 -C, --[no-]color Force colored output of values and bar markers
479 -c, --count Omit repetitions and count the number of
481 -f, --field=([+]N|REGEXP)
482 Compare values after a given number of whitespace
484 --header Prepend a chart axis with minimum and maximum
486 -H, --human-readable Format values using SI unit prefixes
487 --sexagesimal Convert seconds to HH:MM:SS time format
488 -t, --interval[=(N|-LINES)]
489 Output partial progress every given number of
490 seconds or input lines
491 -l, --length=[-]SIZE[%] Trim line contents (between number and bars)
492 -L, --limit=[N|[-]START(-[END]|+N)]
493 Select a range of lines to display
494 -e, --log Logarithmic (exponential) scale instead of linear
495 --graph-format=CHAR Glyph to repeat for the graph line
496 -m, --markers=FORMAT Statistical positions to indicate on bars
497 --min=N, --max=N Bars extend from 0 or the minimum value if lower
498 --palette=(PRESET|COLORS)
499 Override colors of parsed numbers
500 -_, --spark Replace lines by sparklines
501 --indicators[=CHARS] Prefix a unicode character corresponding to each
503 -s, --stat Total statistics after all data
504 -u, --unmodified Do not reformat values, keeping leading whitespace
505 --value-length=SIZE Reserved space for numbers
506 -w, --width=COLUMNS Override the maximum number of columns to use
507 -h, --usage Overview of available options
508 --help Full pod documentation
509 -V, --version Version information
515 barcat - concatenate texts with graph to visualize values
519 B<barcat> [I<options>] [I<file>... | I<numbers>]
523 Visualizes relative sizes of values read from input
524 (parameters, file(s) or STDIN).
525 Contents are concatenated similar to I<cat>,
526 but numbers are reformatted and a bar graph is appended to each line.
528 It can has various options for input and output (re)formatting,
529 but remains limited to one-dimensional charts.
530 For more complex graphing needs
531 you'll need a larger animal like I<gnuplot>.
537 =item B<-a>, B<-->[B<no->]B<ascii>
539 Restrict user interface to ASCII characters,
540 replacing default UTF-8 by their closest approximation.
541 Input is always interpreted as UTF-8 and shown as is.
543 =item B<-C>, B<-->[B<no->]B<color>
545 Force colored output of values and bar markers.
546 Defaults on if output is a tty,
547 disabled otherwise such as when piped or redirected.
548 Can also be disabled by setting B<-M>
549 or the I<NO_COLOR> environment variable.
551 =item B<-c>, B<--count>
553 Omit repetitions and count the number of occurrences.
554 Similar to piping input through C<sort | uniq -c>
555 but keeping the order of first appearances.
557 Lines are omitted if they (or a specified field) are identical,
558 and the amount of matches is prepended and used as values
559 for bars and subsequent statistics.
561 =item B<-f>, B<--field>=([B<+>]I<number> | I<regexp>)
563 Compare values after a given number of whitespace separators,
564 or matching a regular expression.
566 Unspecified or B<-f0> means values are at the start of each line.
567 With B<-f1> the second word is taken instead.
568 A string can indicate the starting position of a value
569 (such as B<-f:> if preceded by colons),
570 or capture the numbers itself,
571 for example B<-f'(\d+)'> for the first digits anywhere.
572 A shorthand for this is C<+0>, or C<+N> to find the Nth number.
576 Prepend a chart axis with minimum and maximum values labeled.
578 =item B<-H>, B<--human-readable>
580 Format values using SI unit prefixes,
581 turning long numbers like C<12356789> into C<12.4M>.
582 Also changes an exponent C<1.602176634e-19> to C<160.2z>.
583 Short integers are aligned but kept without decimal point.
585 =item B<--sexagesimal>
587 Convert seconds to HH:MM:SS time format.
589 =item B<-t>, B<--interval>[=(I<seconds> | B<->I<lines>)]
591 Output partial progress every given number of seconds or input lines.
592 An update can also be forced by sending a I<SIGALRM> alarm signal.
594 =item B<-l>, B<--length>=[B<->]I<size>[B<%>]
596 Trim line contents (between number and bars)
597 to a maximum number of characters.
598 The exceeding part is replaced by an abbreviation sign,
599 unless B<--length=0>.
601 Prepend a dash (i.e. make negative) to enforce padding
602 regardless of encountered contents.
604 =item B<-L>, B<--limit>=[I<count> | [B<->]I<start>(B<->[I<end>] | B<+>I<count>)]
606 Select a range of lines to display.
607 A single integer indicates the last line number (like I<head>),
608 or first line counting from the bottom if negative (like I<tail>).
610 A range consists of a starting line number followed by either
611 a dash C<-> to an optional end, or plus sign C<+> with count.
613 All hidden input is still counted and analyzed for statistics,
614 but disregarded for padding and bar size.
616 =item B<-e>, B<--log>
618 Logarithmic (B<e>xponential) scale instead of linear
619 to compare orders of magnitude.
621 =item B<--graph-format>=I<character>
623 Glyph to repeat for the graph line.
624 Defaults to a dash C<->.
626 =item B<-m>, B<--markers>=I<format>
628 Statistical positions to indicate on bars.
629 A single indicator glyph precedes each position:
635 Exact value to match on the axis.
636 A vertical bar at the zero crossing is displayed by C<|0>
638 For example C<π3.14> would locate pi.
640 =item B</>I<interval>
642 Repeated at every multiple of a number.
643 For example C<:/1> for a grid at every integer.
645 =item I<percentage>B<v>
647 Ranked value at the given percentile,
648 or score at or below which a percentage falls
649 in its frequency distribution (inclusive).
651 The default shows C<+> at C<50v> for the mean or median:
652 the middle value or interpolation between two values.
653 One standard deviation below the median is at about C<68v>.
654 The default includes C<< >31.73v <68.27v >>
655 to encompass all I<normal> results, or 68% of all entries, by I<< <--> >>.
659 Matches the average (arithmetic mean);
660 the sum of all values divided by the number of counted lines.
661 Indicated by default as C<=>.
665 =item B<--min>=I<number>, B<--max>=I<number>
667 Bars extend from 0 or the minimum value if lower,
668 to the largest value encountered.
669 These options can be set to customize this range.
671 =item B<--palette>=(I<preset> | I<color>...)
673 Override colors of parsed numbers.
674 Can be any CSI escape, such as C<90> for default dark gray,
675 or alternatively C<1;30> for bright black.
677 In case of additional colors,
678 the last is used for values equal to the maximum, the first for minima.
679 If unspecified, these are green and red respectively (C<31 90 32>).
680 Multiple intermediate colors will be distributed
681 relative to the size of values.
683 A non-numeric name can refer to a predefined color scheme:
689 Minimal set of monochrome brightnesses.
693 Utilize the 24 grayscale ramp in 256-color terminals.
697 Gradient red to white in 7 out of 16 colors.
701 Extended to 17 colors out of 256.
705 Saturated red to green to blue to red.
709 All 215 extended colors in unrelated orders.
713 =item B<-_>, B<--spark>
715 Replace lines by I<sparklines>,
716 single characters (configured by B<--indicators>)
717 corresponding to input values.
719 =item B<--indicators>[=I<characters>]
721 Prefix a unicode character corresponding to each value.
722 The first specified character will be used for non-values,
723 the remaining sequence will be distributed over the range of values.
724 Unspecified, block fill glyphs U+2581-2588 will be used.
726 =item B<-s>, B<--stat>
728 Total statistics after all data.
730 While processing (possibly a neverending pipe),
731 intermediate results are also shown on signal I<SIGINFO> if available (control+t on BSDs)
732 or I<SIGQUIT> otherwise (ctrl+\ on linux).
734 =item B<-u>, B<--unmodified>
736 Do not reformat values, keeping leading whitespace.
737 Keep original value alignment, which may be significant in some programs.
739 =item B<--value-length>=I<size>
741 Reserved space for numbers.
743 =item B<-w>, B<--width>=I<columns>
745 Override the maximum number of columns to use.
746 Appended graphics will extend to fill up the entire screen,
747 otherwise determined by the environment variable I<COLUMNS>
748 or by running the I<tput> command.
750 =item B<-h>, B<--usage>
752 Overview of available options.
756 Full pod documentation
757 as rendered by perldoc.
759 =item B<-V>, B<--version>
769 seq 30 | awk '{print sin($1/10)}' | barcat
771 Compare file sizes (with human-readable numbers):
773 du -d0 -b * | barcat -H
775 Same from formatted results, selecting the first numeric value:
777 tree -s --noreport | barcat -H -f+
779 Compare media metadata, like image size or play time:
781 exiftool -T -p '$megapixels ($imagesize) $filename' * | barcat
783 exiftool -T -p '$duration# $avgbitrate# $filename' * | barcat --sexagesimal
785 find -type f -print0 | xargs -0 -L1 \
786 ffprobe -show_format -of json -v error |
787 jq -r '.format|.duration+" "+.bit_rate+" "+.filename' | barcat --sex
789 Memory usage of user processes with long names truncated:
791 ps xo rss,pid,cmd | barcat -l40
793 Monitor network latency from prefixed results:
795 ping google.com | barcat -f'time=\K' -t
797 Commonly used after counting, eg letter frequencies in text files:
799 cat /usr/share/games/fortunes/*.u8 |
800 perl -CS -nE 'say for grep length, split /\PL*/, uc' |
801 sort | uniq -c | barcat
803 Users on the current server while preserving order:
805 users | tr ' ' '\n' | barcat -c
807 Number of HTTP requests per day:
809 barcat -cf'\[([^:]+)' httpd/access.log
811 Any kind of database query results, preserving returned alignment:
813 echo 'SELECT sin(value * .1) FROM generate_series(0, 30) value' |
816 In PostgreSQL from within the client; a fancy C<\dt+> perhaps:
818 > SELECT schemaname, relname, pg_total_relation_size(relid)
819 FROM pg_statio_user_tables ORDER BY idx_blks_hit
822 Same thing in SQLite (requires the sqlite3 client):
825 > SELECT name, sum(pgsize) FROM dbstat GROUP BY 1;
827 Earthquakes worldwide magnitude 1+ in the last 24 hours:
829 curl https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/1.0_day.csv |
830 column -ts, -n | barcat -f4 -u -l80%
832 External datasets, like movies per year:
834 curl https://github.com/prust/wikipedia-movie-data/raw/master/movies.json -L |
835 jq .[].year | uniq -c | barcat
837 Pokémon height comparison:
839 curl https://github.com/Biuni/PokemonGO-Pokedex/raw/master/pokedex.json -L |
840 jq -r '.pokemon[] | [.height,.num,.name] | join(" ")' | barcat
842 USD/EUR exchange rate from CSV provided by the ECB:
844 curl https://sdw.ecb.europa.eu/export.do \
845 -Gd 'node=SEARCHRESULTS&q=EXR.D.USD.EUR.SP00.A&exportType=csv' |
846 barcat -f',\K' --value-length=7
848 Total population history in XML from the World Bank:
850 curl http://api.worldbank.org/v2/country/1W/indicator/SP.POP.TOTL |
851 xmlstarlet sel -t -m '*/*' -v wb:date -o ' ' -v wb:value -n |
852 barcat -f1 -H --markers=+/1e9
854 Population and other information for all countries:
856 curl http://download.geonames.org/export/dump/countryInfo.txt |
857 grep -v '^#\s' | column -ts$'\t' -n | barcat -f+2 -e -u -l150 -s
859 And of course various Git statistics, such commit count by year:
861 git log --pretty=%ci | cut -b-4 | uniq -c | barcat
863 Or the top 3 most frequent authors with statistics over all:
865 git shortlog -sn | barcat -L3 -s
867 Activity graph of the last days (substitute date C<-v-{}d> on BSD):
869 ( git log --pretty=%ci --since=30day | cut -b-10
870 seq 0 30 | xargs -i date +%F -d-{}day ) |
871 sort | uniq -c | awk '$1--' | barcat --spark
873 Sparkline graphics of simple input given as inline parameters:
875 barcat -_ 3 1 4 1 5 0 9 2 4
877 Misusing the spark functionality to draw a lolcat line:
879 seq $(tput cols) | barcat --spark --indicator=- --palette=rainbow
883 Mischa POSLAWSKY <perl@shiar.org>