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 eq 'avg') {
293 return sum(@order) / @order;
295 elsif ($func =~ /\A([0-9.]+)v\z/) {
297 "Invalid marker $char: percentile $1 out of bounds\n"
299 my $index = $#order * $1 / 100;
300 return ($order[$index] + $order[$index + .5]) / 2;
302 elsif ($func =~ /\A-?[0-9.]+\z/) {
305 elsif ($func =~ /\A\/($float)\z/) {
306 my @range = my $multiple = my $next = $1;
307 while ($next < $maxval) {
308 $multiple *= 10 if $opt{log};
309 push @range, $next += $multiple;
314 die "Unknown marker $char: $func\n";
323 $pos &&= log $pos if $opt{log};
325 $increment ||= $minval && !$pos;
326 color(36) for $barmark[$pos / $range * $size + $increment + .5] = $char;
330 state $lastmax = $maxval;
331 if ($maxval > $lastmax) {
332 print ' ' x ($lenval + $len);
335 ($lastmax - $minval) * $size / $range + .5,
336 '-' x (($values[$nr - 1] - $minval) * $size / $range);
338 say '+' x (($range - $lastmax) * $size / $range + .5);
345 color(31), sprintf('%*s', $lenval, $minval),
346 color(90), '-', color(36), '+',
347 color(32), sprintf('%*s', $size - 3, $maxval),
348 color(90), '-', color(36), '+',
352 while ($nr <= $limit) {
353 my $val = $values[$nr];
356 $rel = $val - $minval;
357 $rel &&= log $rel if $opt{log};
358 $rel = min(1, $rel / $range) if $range; # 0..1
360 my $color = !length $val || !$opt{palette} ? undef :
361 $val == $order[0] ? $opt{palette}->[-1] : # max
362 $val == $order[-1] ? $opt{palette}->[0] : # min
363 $opt{palette}->[ $rel * ($#{$opt{palette}} - 1) + 1 ];
364 my $indicator = $opt{indicators} && $opt{indicators}->[
365 !length($val) || !$#{$opt{indicators}} ? 0 : # blank
366 $#{$opt{indicators}} < 2 ? 1 :
367 $val >= $order[0] ? -1 :
368 $rel * ($#{$opt{indicators}} - 1e-14) + 1
372 say '' if $opt{width} and $nr and $nr % $opt{width} == 0;
373 print color($color), $_ for $indicator;
376 print $indicator if defined $indicator;
379 $val = sprintf("%*s", $lenval,
380 $opt{reformat} ? $opt{'value-format'}->($val) : $val
382 color($color) for $val;
384 my $line = $lines[$nr] =~ s/\n/$val/r;
385 if (not length $val) {
389 printf '%-*s', $len + length($val), $line;
390 if ($rel and $size) {
391 print $barmark[$_] // $opt{'graph-format'}
392 for 1 .. $rel * $size + .5;
399 say $opt{palette} ? color(0) : '' if $opt{spark};
400 %uniq = () if $opt{interval} and $opt{count};
410 my $linemin = !$opt{hidemin} ? 0 :
411 ($vars{start} = $opt{hidemin}->($#lines));
412 my $linemax = !$opt{hidemax} ? $#lines :
413 ($vars{end} = $opt{hidemax}->($#lines, $vars{start}));
415 $vars{partsum} = sum(0, grep {length} @values[$linemin .. $linemax])
416 if $linemin <= $linemax and ($opt{hidemin} or $opt{hidemax});
422 $vars{avg} = $vars{sum} / @order;
424 say varfmt($opt{report}, \%vars);
429 my ($fmt, $vars) = @_;
430 $fmt =~ s[\$\{ \h*+ ((?: [^{}]++ | \{(?1)\} )+) \}]{
431 my ($name, $op, $cmd) = split /\s*([;:])/, $1, 2;
432 my $format = $name =~ s/\+// || $name !~ s/\#// && $opt{reformat};
433 local $_ = $vars->{$name};
435 $_ = $opt{'value-format'}->($_) if $format;
436 if ($cmd and $op eq ':') {
437 $_ = varfmt($cmd, $vars);
441 warn "Error in \$$name report: $@" if $@;
451 show_stat() if $opt{stat};
452 exit 130 if @_; # 0x80+signo
460 barcat [OPTIONS] [FILES|NUMBERS] (=•.•=)
463 -a, --[no-]ascii Restrict user interface to ASCII characters
464 -C, --[no-]color Force colored output of values and bar markers
465 -c, --count Omit repetitions and count the number of
467 -f, --field=([+]N|REGEXP)
468 Compare values after a given number of whitespace
470 --header Prepend a chart axis with minimum and maximum
472 -H, --human-readable Format values using SI unit prefixes
473 --sexagesimal Convert seconds to HH:MM:SS time format
474 -t, --interval[=(N|-LINES)]
475 Output partial progress every given number of
476 seconds or input lines
477 -l, --length=[-]SIZE[%] Trim line contents (between number and bars)
478 -L, --limit=[N|[-]START(-[END]|+N)]
479 Select a range of lines to display
480 -e, --log Logarithmic (exponential) scale instead of linear
481 --graph-format=CHAR Glyph to repeat for the graph line
482 -m, --markers=FORMAT Statistical positions to indicate on bars
483 --min=N, --max=N Bars extend from 0 or the minimum value if lower
484 --palette=(PRESET|COLORS)
485 Override colors of parsed numbers
486 -_, --spark Replace lines by sparklines
487 --indicators[=CHARS] Prefix a unicode character corresponding to each
489 -s, --stat Total statistics after all data
490 -u, --unmodified Do not reformat values, keeping leading whitespace
491 --value-length=SIZE Reserved space for numbers
492 -w, --width=COLUMNS Override the maximum number of columns to use
493 -h, --usage Overview of available options
494 --help Full pod documentation
495 -V, --version Version information
501 barcat - concatenate texts with graph to visualize values
505 B<barcat> [I<options>] [I<file>... | I<numbers>]
509 Visualizes relative sizes of values read from input
510 (parameters, file(s) or STDIN).
511 Contents are concatenated similar to I<cat>,
512 but numbers are reformatted and a bar graph is appended to each line.
514 It can has various options for input and output (re)formatting,
515 but remains limited to one-dimensional charts.
516 For more complex graphing needs
517 you'll need a larger animal like I<gnuplot>.
523 =item B<-a>, B<-->[B<no->]B<ascii>
525 Restrict user interface to ASCII characters,
526 replacing default UTF-8 by their closest approximation.
527 Input is always interpreted as UTF-8 and shown as is.
529 =item B<-C>, B<-->[B<no->]B<color>
531 Force colored output of values and bar markers.
532 Defaults on if output is a tty,
533 disabled otherwise such as when piped or redirected.
534 Can also be disabled by setting B<-M>
535 or the I<NO_COLOR> environment variable.
537 =item B<-c>, B<--count>
539 Omit repetitions and count the number of occurrences.
540 Similar to piping input through C<sort | uniq -c>
541 but keeping the order of first appearances.
543 Lines are omitted if they (or a specified field) are identical,
544 and the amount of matches is prepended and used as values
545 for bars and subsequent statistics.
547 =item B<-f>, B<--field>=([B<+>]I<number> | I<regexp>)
549 Compare values after a given number of whitespace separators,
550 or matching a regular expression.
552 Unspecified or B<-f0> means values are at the start of each line.
553 With B<-f1> the second word is taken instead.
554 A string can indicate the starting position of a value
555 (such as B<-f:> if preceded by colons),
556 or capture the numbers itself,
557 for example B<-f'(\d+)'> for the first digits anywhere.
558 A shorthand for this is C<+0>, or C<+N> to find the Nth number.
562 Prepend a chart axis with minimum and maximum values labeled.
564 =item B<-H>, B<--human-readable>
566 Format values using SI unit prefixes,
567 turning long numbers like C<12356789> into C<12.4M>.
568 Also changes an exponent C<1.602176634e-19> to C<160.2z>.
569 Short integers are aligned but kept without decimal point.
571 =item B<--sexagesimal>
573 Convert seconds to HH:MM:SS time format.
575 =item B<-t>, B<--interval>[=(I<seconds> | B<->I<lines>)]
577 Output partial progress every given number of seconds or input lines.
578 An update can also be forced by sending a I<SIGALRM> alarm signal.
580 =item B<-l>, B<--length>=[B<->]I<size>[B<%>]
582 Trim line contents (between number and bars)
583 to a maximum number of characters.
584 The exceeding part is replaced by an abbreviation sign,
585 unless B<--length=0>.
587 Prepend a dash (i.e. make negative) to enforce padding
588 regardless of encountered contents.
590 =item B<-L>, B<--limit>=[I<count> | [B<->]I<start>(B<->[I<end>] | B<+>I<count>)]
592 Select a range of lines to display.
593 A single integer indicates the last line number (like I<head>),
594 or first line counting from the bottom if negative (like I<tail>).
596 A range consists of a starting line number followed by either
597 a dash C<-> to an optional end, or plus sign C<+> with count.
599 All hidden input is still counted and analyzed for statistics,
600 but disregarded for padding and bar size.
602 =item B<-e>, B<--log>
604 Logarithmic (B<e>xponential) scale instead of linear
605 to compare orders of magnitude.
607 =item B<--graph-format>=I<character>
609 Glyph to repeat for the graph line.
610 Defaults to a dash C<->.
612 =item B<-m>, B<--markers>=I<format>
614 Statistical positions to indicate on bars.
615 A single indicator glyph precedes each position:
621 Exact value to match on the axis.
622 A vertical bar at the zero crossing is displayed by C<|0>
624 For example C<π3.14> would locate pi.
626 =item B</>I<interval>
628 Repeated at every multiple of a number.
629 For example C<:/1> for a grid at every integer.
631 =item I<percentage>B<v>
633 Ranked value at the given percentile.
634 The default shows C<+> at C<50v> for the mean or median;
635 the middle value or average between middle values.
636 One standard deviation right of the mean is at about C<68.3v>.
637 The default includes C<< >31.73v <68.27v >>
638 to encompass all I<normal> results, or 68% of all entries, by I<< <--> >>.
643 the sum of all values divided by the number of counted lines.
644 Indicated by default as C<=>.
648 =item B<--min>=I<number>, B<--max>=I<number>
650 Bars extend from 0 or the minimum value if lower,
651 to the largest value encountered.
652 These options can be set to customize this range.
654 =item B<--palette>=(I<preset> | I<color>...)
656 Override colors of parsed numbers.
657 Can be any CSI escape, such as C<90> for default dark gray,
658 or alternatively C<1;30> for bright black.
660 In case of additional colors,
661 the last is used for values equal to the maximum, the first for minima.
662 If unspecified, these are green and red respectively (C<31 90 32>).
663 Multiple intermediate colors will be distributed
664 relative to the size of values.
666 A non-numeric name can refer to a predefined color scheme:
672 Minimal set of monochrome brightnesses.
676 Utilize the 24 grayscale ramp in 256-color terminals.
680 Gradient red to white in 7 out of 16 colors.
684 Extended to 17 colors out of 256.
688 Saturated red to green to blue to red.
692 All 215 extended colors in unrelated orders.
696 =item B<-_>, B<--spark>
698 Replace lines by I<sparklines>,
699 single characters (configured by B<--indicators>)
700 corresponding to input values.
702 =item B<--indicators>[=I<characters>]
704 Prefix a unicode character corresponding to each value.
705 The first specified character will be used for non-values,
706 the remaining sequence will be distributed over the range of values.
707 Unspecified, block fill glyphs U+2581-2588 will be used.
709 =item B<-s>, B<--stat>
711 Total statistics after all data.
713 While processing (possibly a neverending pipe),
714 intermediate results are also shown on signal I<SIGINFO> if available (control+t on BSDs)
715 or I<SIGQUIT> otherwise (ctrl+\ on linux).
717 =item B<-u>, B<--unmodified>
719 Do not reformat values, keeping leading whitespace.
720 Keep original value alignment, which may be significant in some programs.
722 =item B<--value-length>=I<size>
724 Reserved space for numbers.
726 =item B<-w>, B<--width>=I<columns>
728 Override the maximum number of columns to use.
729 Appended graphics will extend to fill up the entire screen,
730 otherwise determined by the environment variable I<COLUMNS>
731 or by running the I<tput> command.
733 =item B<-h>, B<--usage>
735 Overview of available options.
739 Full pod documentation
740 as rendered by perldoc.
742 =item B<-V>, B<--version>
752 seq 30 | awk '{print sin($1/10)}' | barcat
754 Compare file sizes (with human-readable numbers):
756 du -d0 -b * | barcat -H
758 Same from formatted results, selecting the first numeric value:
760 tree -s --noreport | barcat -H -f+
762 Compare media metadata, like image size or play time:
764 exiftool -T -p '$megapixels ($imagesize) $filename' * | barcat
766 exiftool -T -p '$duration# $avgbitrate# $filename' * | barcat --sexagesimal
768 find -type f -print0 | xargs -0 -L1 \
769 ffprobe -show_format -of json -v error |
770 jq -r '.format|.duration+" "+.bit_rate+" "+.filename' | barcat --sex
772 Memory usage of user processes with long names truncated:
774 ps xo rss,pid,cmd | barcat -l40
776 Monitor network latency from prefixed results:
778 ping google.com | barcat -f'time=\K' -t
780 Commonly used after counting, eg letter frequencies in text files:
782 cat /usr/share/games/fortunes/*.u8 |
783 perl -CS -nE 'say for grep length, split /\PL*/, uc' |
784 sort | uniq -c | barcat
786 Users on the current server while preserving order:
788 users | tr ' ' '\n' | barcat -c
790 Number of HTTP requests per day:
792 barcat -cf'\[([^:]+)' httpd/access.log
794 Any kind of database query results, preserving returned alignment:
796 echo 'SELECT sin(value * .1) FROM generate_series(0, 30) value' |
799 In PostgreSQL from within the client; a fancy C<\dt+> perhaps:
801 > SELECT schemaname, relname, pg_total_relation_size(relid)
802 FROM pg_statio_user_tables ORDER BY idx_blks_hit
805 Same thing in SQLite (requires the sqlite3 client):
808 > SELECT name, sum(pgsize) FROM dbstat GROUP BY 1;
810 Earthquakes worldwide magnitude 1+ in the last 24 hours:
812 curl https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/1.0_day.csv |
813 column -ts, -n | barcat -f4 -u -l80%
815 External datasets, like movies per year:
817 curl https://github.com/prust/wikipedia-movie-data/raw/master/movies.json -L |
818 jq .[].year | uniq -c | barcat
820 Pokémon height comparison:
822 curl https://github.com/Biuni/PokemonGO-Pokedex/raw/master/pokedex.json -L |
823 jq -r '.pokemon[] | [.height,.num,.name] | join(" ")' | barcat
825 USD/EUR exchange rate from CSV provided by the ECB:
827 curl https://sdw.ecb.europa.eu/export.do \
828 -Gd 'node=SEARCHRESULTS&q=EXR.D.USD.EUR.SP00.A&exportType=csv' |
829 barcat -f',\K' --value-length=7
831 Total population history in XML from the World Bank:
833 curl http://api.worldbank.org/v2/country/1W/indicator/SP.POP.TOTL |
834 xmlstarlet sel -t -m '*/*' -v wb:date -o ' ' -v wb:value -n |
835 barcat -f1 -H --markers=+/1e9
837 Population and other information for all countries:
839 curl http://download.geonames.org/export/dump/countryInfo.txt |
840 grep -v '^#\s' | column -ts$'\t' -n | barcat -f+2 -e -u -l150 -s
842 And of course various Git statistics, such commit count by year:
844 git log --pretty=%ci | cut -b-4 | uniq -c | barcat
846 Or the top 3 most frequent authors with statistics over all:
848 git shortlog -sn | barcat -L3 -s
850 Activity graph of the last days (substitute date C<-v-{}d> on BSD):
852 ( git log --pretty=%ci --since=30day | cut -b-10
853 seq 0 30 | xargs -i date +%F -d-{}day ) |
854 sort | uniq -c | awk '$1--' | barcat --spark
856 Sparkline graphics of simple input given as inline parameters:
858 barcat -_ 3 1 4 1 5 0 9 2 4
860 Misusing the spark functionality to draw a lolcat line:
862 seq $(tput cols) | barcat --spark --indicator=- --palette=rainbow
866 Mischa POSLAWSKY <perl@shiar.org>