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;
291 if ($func eq 'avg') {
292 return sum(@order) / @order;
294 elsif ($func =~ /\A([0-9.]+)v\z/) {
296 "Invalid marker $char: percentile $1 out of bounds\n"
298 my $index = $#order * $1 / 100;
299 return ($order[$index] + $order[$index + .5]) / 2;
301 elsif ($func =~ /\A-?[0-9.]+\z/) {
304 elsif ($func =~ /\A\/($float)\z/) {
305 my @range = my $multiple = my $next = $1;
306 while ($next < $maxval) {
307 $multiple *= 10 if $opt{log};
308 push @range, $next += $multiple;
313 die "Unknown marker $char: $func\n";
322 $pos &&= log $pos if $opt{log};
324 color(36) for $barmark[$pos / $range * $size] = $char;
328 state $lastmax = $maxval;
329 if ($maxval > $lastmax) {
330 print ' ' x ($lenval + $len);
333 ($lastmax - $minval) * $size / $range + .5,
334 '-' x (($values[$nr - 1] - $minval) * $size / $range);
336 say '+' x (($range - $lastmax) * $size / $range + .5);
343 color(31), sprintf('%*s', $lenval, $minval),
344 color(90), '-', color(36), '+',
345 color(32), sprintf('%*s', $size - 3, $maxval),
346 color(90), '-', color(36), '+',
350 while ($nr <= $limit) {
351 my $val = $values[$nr];
354 $rel = $val - $minval;
355 $rel &&= log $rel if $opt{log};
356 $rel = min(1, $rel / $range) if $range; # 0..1
358 my $color = !length $val || !$opt{palette} ? undef :
359 $val == $order[0] ? $opt{palette}->[-1] : # max
360 $val == $order[-1] ? $opt{palette}->[0] : # min
361 $opt{palette}->[ $rel * ($#{$opt{palette}} - 1) + 1 ];
362 my $indicator = $opt{indicators} && $opt{indicators}->[
363 !length($val) || !$#{$opt{indicators}} ? 0 : # blank
364 $#{$opt{indicators}} < 2 ? 1 :
365 $val >= $order[0] ? -1 :
366 $rel * ($#{$opt{indicators}} - 1e-14) + 1
370 say '' if $opt{width} and $nr and $nr % $opt{width} == 0;
371 print color($color), $_ for $indicator;
374 print $indicator if defined $indicator;
377 $val = sprintf("%*s", $lenval,
378 $opt{reformat} ? $opt{'value-format'}->($val) : $val
380 color($color) for $val;
382 my $line = $lines[$nr] =~ s/\n/$val/r;
383 if (not length $val) {
387 printf '%-*s', $len + length($val), $line;
388 if ($rel and $size) {
389 print $barmark[$_] // $opt{'graph-format'}
390 for 1 .. $rel * $size + .5;
397 say $opt{palette} ? color(0) : '' if $opt{spark};
398 %uniq = () if $opt{interval} and $opt{count};
408 my $linemin = !$opt{hidemin} ? 0 :
409 ($vars{start} = $opt{hidemin}->($#lines));
410 my $linemax = !$opt{hidemax} ? $#lines :
411 ($vars{end} = $opt{hidemax}->($#lines, $vars{start}));
413 $vars{partsum} = sum(0, grep {length} @values[$linemin .. $linemax])
414 if $linemin <= $linemax and ($opt{hidemin} or $opt{hidemax});
420 $vars{avg} = $vars{sum} / @order;
422 say varfmt($opt{report}, \%vars);
427 my ($fmt, $vars) = @_;
428 $fmt =~ s[\$\{ \h*+ ((?: [^{}]++ | \{(?1)\} )+) \}]{
429 my ($name, $op, $cmd) = split /\s*([;:])/, $1, 2;
430 my $format = $name =~ s/\+// || $name !~ s/\#// && $opt{reformat};
431 local $_ = $vars->{$name};
433 $_ = $opt{'value-format'}->($_) if $format;
434 if ($cmd and $op eq ':') {
435 $_ = varfmt($cmd, $vars);
439 warn "Error in \$$name report: $@" if $@;
449 show_stat() if $opt{stat};
450 exit 130 if @_; # 0x80+signo
458 barcat [OPTIONS] [FILES|NUMBERS] (=•.•=)
461 -a, --[no-]ascii Restrict user interface to ASCII characters
462 -C, --[no-]color Force colored output of values and bar markers
463 -c, --count Omit repetitions and count the number of
465 -f, --field=([+]N|REGEXP)
466 Compare values after a given number of whitespace
468 --header Prepend a chart axis with minimum and maximum
470 -H, --human-readable Format values using SI unit prefixes
471 --sexagesimal Convert seconds to HH:MM:SS time format
472 -t, --interval[=(N|-LINES)]
473 Output partial progress every given number of
474 seconds or input lines
475 -l, --length=[-]SIZE[%] Trim line contents (between number and bars)
476 -L, --limit=[N|[-]START(-[END]|+N)]
477 Select a range of lines to display
478 -e, --log Logarithmic (exponential) scale instead of linear
479 --graph-format=CHAR Glyph to repeat for the graph line
480 -m, --markers=FORMAT Statistical positions to indicate on bars
481 --min=N, --max=N Bars extend from 0 or the minimum value if lower
482 --palette=(PRESET|COLORS)
483 Override colors of parsed numbers
484 -_, --spark Replace lines by sparklines
485 --indicators[=CHARS] Prefix a unicode character corresponding to each
487 -s, --stat Total statistics after all data
488 -u, --unmodified Do not reformat values, keeping leading whitespace
489 --value-length=SIZE Reserved space for numbers
490 -w, --width=COLUMNS Override the maximum number of columns to use
491 -h, --usage Overview of available options
492 --help Full pod documentation
493 -V, --version Version information
499 barcat - concatenate texts with graph to visualize values
503 B<barcat> [I<options>] [I<file>... | I<numbers>]
507 Visualizes relative sizes of values read from input
508 (parameters, file(s) or STDIN).
509 Contents are concatenated similar to I<cat>,
510 but numbers are reformatted and a bar graph is appended to each line.
512 Don't worry, barcat does not drink and divide.
513 It can has various options for input and output (re)formatting,
514 but remains limited to one-dimensional charts.
515 For more complex graphing needs
516 you'll need a larger animal like I<gnuplot>.
522 =item B<-a>, B<-->[B<no->]B<ascii>
524 Restrict user interface to ASCII characters,
525 replacing default UTF-8 by their closest approximation.
526 Input is always interpreted as UTF-8 and shown as is.
528 =item B<-C>, B<-->[B<no->]B<color>
530 Force colored output of values and bar markers.
531 Defaults on if output is a tty,
532 disabled otherwise such as when piped or redirected.
533 Can also be disabled by setting B<-M>
534 or the I<NO_COLOR> environment variable.
536 =item B<-c>, B<--count>
538 Omit repetitions and count the number of occurrences.
539 Similar to piping input through C<sort | uniq -c>
540 but keeping the order of first appearances.
542 Lines are omitted if they (or a specified field) are identical,
543 and the amount of matches is prepended and used as values
544 for bars and subsequent statistics.
546 =item B<-f>, B<--field>=([B<+>]I<number> | I<regexp>)
548 Compare values after a given number of whitespace separators,
549 or matching a regular expression.
551 Unspecified or B<-f0> means values are at the start of each line.
552 With B<-f1> the second word is taken instead.
553 A string can indicate the starting position of a value
554 (such as B<-f:> if preceded by colons),
555 or capture the numbers itself,
556 for example B<-f'(\d+)'> for the first digits anywhere.
557 A shorthand for this is C<+0>, or C<+N> to find the Nth number.
561 Prepend a chart axis with minimum and maximum values labeled.
563 =item B<-H>, B<--human-readable>
565 Format values using SI unit prefixes,
566 turning long numbers like C<12356789> into C<12.4M>.
567 Also changes an exponent C<1.602176634e-19> to C<160.2z>.
568 Short integers are aligned but kept without decimal point.
570 =item B<--sexagesimal>
572 Convert seconds to HH:MM:SS time format.
574 =item B<-t>, B<--interval>[=(I<seconds> | B<->I<lines>)]
576 Output partial progress every given number of seconds or input lines.
577 An update can also be forced by sending a I<SIGALRM> alarm signal.
579 =item B<-l>, B<--length>=[B<->]I<size>[B<%>]
581 Trim line contents (between number and bars)
582 to a maximum number of characters.
583 The exceeding part is replaced by an abbreviation sign,
584 unless B<--length=0>.
586 Prepend a dash (i.e. make negative) to enforce padding
587 regardless of encountered contents.
589 =item B<-L>, B<--limit>=[I<count> | [B<->]I<start>(B<->[I<end>] | B<+>I<count>)]
591 Select a range of lines to display.
592 A single integer indicates the last line number (like I<head>),
593 or first line counting from the bottom if negative (like I<tail>).
595 A range consists of a starting line number followed by either
596 a dash C<-> to an optional end, or plus sign C<+> with count.
598 All hidden input is still counted and analyzed for statistics,
599 but disregarded for padding and bar size.
601 =item B<-e>, B<--log>
603 Logarithmic (B<e>xponential) scale instead of linear
604 to compare orders of magnitude.
606 =item B<--graph-format>=I<character>
608 Glyph to repeat for the graph line.
609 Defaults to a dash C<->.
611 =item B<-m>, B<--markers>=I<format>
613 Statistical positions to indicate on bars.
614 A single indicator glyph precedes each position:
620 Exact value to match on the axis.
621 A vertical bar at the zero crossing is displayed by C<|0>
623 For example C<π3.14> would locate pi.
625 =item B</>I<interval>
627 Repeated at every multiple of a number.
628 For example C<:/1> for a grid at every integer.
630 =item I<percentage>B<v>
632 Ranked value at the given percentile.
633 The default shows C<+> at C<50v> for the mean or median;
634 the middle value or average between middle values.
635 One standard deviation right of the mean is at about C<68.3v>.
636 The default includes C<< >31.73v <68.27v >>
637 to encompass all I<normal> results, or 68% of all entries, by I<< <--> >>.
642 the sum of all values divided by the number of counted lines.
643 Indicated by default as C<=>.
647 =item B<--min>=I<number>, B<--max>=I<number>
649 Bars extend from 0 or the minimum value if lower,
650 to the largest value encountered.
651 These options can be set to customize this range.
653 =item B<--palette>=(I<preset> | I<color>...)
655 Override colors of parsed numbers.
656 Can be any CSI escape, such as C<90> for default dark gray,
657 or alternatively C<1;30> for bright black.
659 In case of additional colors,
660 the last is used for values equal to the maximum, the first for minima.
661 If unspecified, these are green and red respectively (C<31 90 32>).
662 Multiple intermediate colors will be distributed
663 relative to the size of values.
665 A non-numeric name can refer to a predefined color scheme:
671 Minimal set of monochrome brightnesses.
675 Utilize the 24 grayscale ramp in 256-color terminals.
679 Gradient red to white in 7 out of 16 colors.
683 Extended to 17 colors out of 256.
687 Saturated red to green to blue to red.
691 All 215 extended colors in unrelated orders.
695 =item B<-_>, B<--spark>
697 Replace lines by I<sparklines>,
698 single characters (configured by B<--indicators>)
699 corresponding to input values.
701 =item B<--indicators>[=I<characters>]
703 Prefix a unicode character corresponding to each value.
704 The first specified character will be used for non-values,
705 the remaining sequence will be distributed over the range of values.
706 Unspecified, block fill glyphs U+2581-2588 will be used.
708 =item B<-s>, B<--stat>
710 Total statistics after all data.
712 While processing (possibly a neverending pipe),
713 intermediate results are also shown on signal I<SIGINFO> if available (control+t on BSDs)
714 or I<SIGQUIT> otherwise (ctrl+\ on linux).
716 =item B<-u>, B<--unmodified>
718 Do not reformat values, keeping leading whitespace.
719 Keep original value alignment, which may be significant in some programs.
721 =item B<--value-length>=I<size>
723 Reserved space for numbers.
725 =item B<-w>, B<--width>=I<columns>
727 Override the maximum number of columns to use.
728 Appended graphics will extend to fill up the entire screen,
729 otherwise determined by the environment variable I<COLUMNS>
730 or by running the I<tput> command.
732 =item B<-h>, B<--usage>
734 Overview of available options.
738 Full pod documentation
739 as rendered by perldoc.
741 =item B<-V>, B<--version>
751 seq 30 | awk '{print sin($1/10)}' | barcat
753 Compare file sizes (with human-readable numbers):
755 du -d0 -b * | barcat -H
757 Same from formatted results, selecting the first numeric value:
759 tree -s --noreport | barcat -H -f+
761 Compare media metadata, like image size or play time:
763 exiftool -T -p '$megapixels ($imagesize) $filename' * | barcat
765 exiftool -T -p '$duration# $avgbitrate# $filename' * | barcat --sexagesimal
767 find -type f -print0 | xargs -0 -L1 \
768 ffprobe -show_format -of json -v error |
769 jq -r '.format|.duration+" "+.bit_rate+" "+.filename' | barcat --sex
771 Memory usage of user processes with long names truncated:
773 ps xo rss,pid,cmd | barcat -l40
775 Monitor network latency from prefixed results:
777 ping google.com | barcat -f'time=\K' -t
779 Commonly used after counting, eg letter frequencies in text files:
781 cat /usr/share/games/fortunes/*.u8 |
782 perl -CS -nE 'say for grep length, split /\PL*/, uc' |
783 sort | uniq -c | barcat
785 Users on the current server while preserving order:
787 users | tr ' ' '\n' | barcat -c
789 Number of HTTP requests per day:
791 barcat -cf'\[([^:]+)' httpd/access.log
793 Any kind of database query results, preserving returned alignment:
795 echo 'SELECT sin(value * .1) FROM generate_series(0, 30) value' |
798 In PostgreSQL from within the client; a fancy C<\dt+> perhaps:
800 > SELECT schemaname, relname, pg_total_relation_size(relid)
801 FROM pg_statio_user_tables ORDER BY idx_blks_hit
804 Same thing in SQLite (requires the sqlite3 client):
807 > SELECT name, sum(pgsize) FROM dbstat GROUP BY 1;
809 Earthquakes worldwide magnitude 1+ in the last 24 hours:
811 curl https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/1.0_day.csv |
812 column -ts, -n | barcat -f4 -u -l80%
814 External datasets, like movies per year:
816 curl https://github.com/prust/wikipedia-movie-data/raw/master/movies.json -L |
817 jq .[].year | uniq -c | barcat
819 Pokémon height comparison:
821 curl https://github.com/Biuni/PokemonGO-Pokedex/raw/master/pokedex.json -L |
822 jq -r '.pokemon[] | [.height,.num,.name] | join(" ")' | barcat
824 USD/EUR exchange rate from CSV provided by the ECB:
826 curl https://sdw.ecb.europa.eu/export.do \
827 -Gd 'node=SEARCHRESULTS&q=EXR.D.USD.EUR.SP00.A&exportType=csv' |
828 barcat -f',\K' --value-length=7
830 Total population history in XML from the World Bank:
832 curl http://api.worldbank.org/v2/country/1W/indicator/SP.POP.TOTL |
833 xmlstarlet sel -t -m '*/*' -v wb:date -o ' ' -v wb:value -n |
834 barcat -f1 -H --markers=+/1e9
836 Population and other information for all countries:
838 curl http://download.geonames.org/export/dump/countryInfo.txt |
839 grep -v '^#\s' | column -ts$'\t' -n | barcat -f+2 -e -u -l150 -s
841 And of course various Git statistics, such commit count by year:
843 git log --pretty=%ci | cut -b-4 | uniq -c | barcat
845 Or the top 3 most frequent authors with statistics over all:
847 git shortlog -sn | barcat -L3 -s
849 Activity graph of the last days (substitute date C<-v-{}d> on BSD):
851 ( git log --pretty=%ci --since=30day | cut -b-10
852 seq 0 30 | xargs -i date +%F -d-{}day ) |
853 sort | uniq -c | awk '$1--' | barcat --spark
855 Sparkline graphics of simple input given as inline parameters:
857 barcat -_ 3 1 4 1 5 0 9 2 4
859 Misusing the spark functionality to draw a lolcat line:
861 seq $(tput cols) | barcat --spark --indicator=- --palette=rainbow
865 Mischa POSLAWSKY <perl@shiar.org>