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{anchor} //= qr/\A/;
146 $opt{'value-length'} = 4 if $opt{units};
147 $opt{'value-length'} = 1 if $opt{unmodified};
148 $opt{'signal-stat'} //= exists $SIG{INFO} ? 'INFO' : 'QUIT';
149 $opt{markers} //= '=avg >31.73v <68.27v +50v |0';
150 $opt{report} //= join('',
151 '${partsum+; $_ .= " of "}',
152 '${sum+; color(1); $_ .= " total in "}',
154 '${lines#; $_ = $_ != @order && " over $_ lines"}',
155 sprintf('${count: (%s)}', join ', ',
156 '${min; color(31)} min',
157 '${avg; $opt{reformat} or $_ = sprintf "%0.2f", $_; color(36)} avg',
158 '${max; color(32)} max',
161 $opt{palette} //= $opt{color} && [31, 90, 32];
162 $opt{indicators} = [split //, $opt{indicators} ||
163 ($opt{ascii} ? ' .oO' : $opt{spark} ? ' ▁▂▃▄▅▆▇█' : ' ▏▎▍▌▋▊▉█')
164 ] if defined $opt{indicators} or $opt{spark};
165 $opt{input} = (@ARGV && $ARGV[0] =~ m/\A[-0-9]/) ? \@ARGV : undef
166 and undef $opt{interval};
168 $opt{'calc-format'} = sub { sprintf '%*.*f', 0, 2, $_[0] };
169 $opt{'value-format'} = $opt{sexagesimal} ? sub {
170 my $s = abs($_[0]) + .5;
171 sprintf('%s%d:%02d:%02d', $_[0] < 0 && '-', $s/3600, $s/60%60, $s%60);
172 } : $opt{units} && sub {
174 log(abs $_[0] || 1) / log(10)
175 - 3 * (abs($_[0]) < .9995) # shift to smaller unit if below 1
176 + 1e-15 # float imprecision
178 my $decimal = ($unit % 3) == ($unit < 0);
179 $unit -= log($decimal ? .995 : .9995) / log(10); # rounded
180 $decimal = ($unit % 3) == ($unit < 0);
181 $decimal &&= $_[0] !~ /^-?0*[0-9]{1,3}$/; # integer 0..999
183 3 + ($_[0] < 0), # digits plus optional negative sign
185 $_[0] / 1000 ** int($unit/3), # number
186 $#{$opt{units}} * 1.5 < abs $unit ? sprintf('e%d', $unit) :
187 $opt{units}->[$unit/3] # suffix
189 } and $opt{reformat}++;
190 $opt{'value-format'} ||= sub { sprintf '%.8g', $_[0] };
193 my (@lines, @values, @order, %uniq);
195 $SIG{$_} = \&show_stat for $opt{'signal-stat'} || ();
198 alarm $opt{interval} if defined $opt{interval} and $opt{interval} > 0;
200 $SIG{INT} = \&show_exit;
202 if (defined $opt{interval}) {
203 $opt{interval} ||= 1;
204 alarm $opt{interval} if $opt{interval} > 0;
207 require Tie::Array::Sorted;
208 tie @order, 'Tie::Array::Sorted', sub { $_[1] <=> $_[0] };
209 } or warn $@, "Expect slowdown with large datasets!\n";
212 my $float = qr<[0-9]* [.]? [0-9]+ (?: e[+-]?[0-9]+ )?>; # positive numberish
213 my $valmatch = qr< $opt{anchor} ( \h* -? $float |) >x;
214 while (defined ($_ = $opt{input} ? shift @{ $opt{input} } : readline)) {
217 my ($valnum) = m/(\S*)/;
219 $uniq{$valnum}++ and next;
220 push @lines, "\n " . $_;
221 push @values, $valnum;
224 s/\A\h*// unless $opt{unmodified};
225 my $valnum = s/$valmatch/\n/ && $1;
226 push @values, $valnum;
227 push @order, $valnum if length $valnum;
228 if (defined $opt{trim} and defined $valnum) {
229 my $trimpos = abs $opt{trim};
230 $trimpos -= length $valnum if $opt{unmodified};
232 $_ = substr $_, 0, 2;
234 elsif (length > $trimpos) {
235 # cut and replace (intentional lvalue for speed, contrary to PBP)
236 substr($_, $trimpos - 1) = $opt{ascii} ? '>' : '…';
240 show_lines() if defined $opt{interval} and $opt{interval} < 0
241 and $. % $opt{interval} == 0;
244 $SIG{INT} = 'DEFAULT';
247 $opt{color} and defined $_[0] or return '';
248 return "\e[$_[0]m" if defined wantarray;
249 $_ = color(@_) . $_ . color(0) if defined;
254 state $nr = $opt{hidemin} ? $opt{hidemin}->($#lines) : 0;
255 @lines > $nr or return;
257 my $limit = $opt{hidemax} ? $opt{hidemax}->($#lines, $nr) : $#lines;
260 $_ = $uniq{$_} for @values;
264 @order = sort { $b <=> $a } @order unless tied @order;
265 my $maxval = $opt{maxval} // (
266 $opt{hidemax} ? max grep { length } @values[$nr .. $limit] :
269 my $minval = $opt{minval} // min $order[-1] // (), 0;
270 my $range = $maxval - $minval;
271 $range &&= log $range if $opt{log};
272 my $lenval = $opt{'value-length'} // max map { length } @order;
273 my $len = defined $opt{trim} && $opt{trim} <= 0 ? -$opt{trim} + 1 :
274 max(map { length $values[$_] && length $lines[$_] } $nr .. $limit)
276 my $size = defined $opt{width} && $range &&
277 ($opt{width} - $lenval - $len - !!$opt{indicators}); # bar multiplication
280 if ($opt{markers} and $size > 0) {
281 for my $markspec (split /\h/, $opt{markers}) {
282 my ($char, $func) = split //, $markspec, 2;
284 if ($func eq 'avg') {
285 return sum(@order) / @order;
287 elsif ($func =~ /\A([0-9.]+)v\z/) {
289 "Invalid marker $char: percentile $1 out of bounds\n"
291 my $index = $#order * $1 / 100;
292 return ($order[$index] + $order[$index + .5]) / 2;
294 elsif ($func =~ /\A-?[0-9.]+\z/) {
297 elsif ($func =~ /\A\/($float)\z/) {
298 my @range = my $multiple = my $next = $1;
299 while ($next < $maxval) {
300 $multiple *= 10 if $opt{log};
301 push @range, $next += $multiple;
306 die "Unknown marker $char: $func\n";
315 $pos &&= log $pos if $opt{log};
317 color(36) for $barmark[$pos / $range * $size] = $char;
321 state $lastmax = $maxval;
322 if ($maxval > $lastmax) {
323 print ' ' x ($lenval + $len);
326 ($lastmax - $minval) * $size / $range + .5,
327 '-' x (($values[$nr - 1] - $minval) * $size / $range);
329 say '+' x (($range - $lastmax) * $size / $range + .5);
336 color(31), sprintf('%*s', $lenval, $minval),
337 color(90), '-', color(36), '+',
338 color(32), sprintf('%*s', $size - 3, $maxval),
339 color(90), '-', color(36), '+',
343 while ($nr <= $limit) {
344 my $val = $values[$nr];
347 $rel = $val - $minval;
348 $rel &&= log $rel if $opt{log};
349 $rel = min(1, $rel / $range) if $range; # 0..1
351 my $color = !length $val || !$opt{palette} ? undef :
352 $val == $order[0] ? $opt{palette}->[-1] : # max
353 $val == $order[-1] ? $opt{palette}->[0] : # min
354 $opt{palette}->[ $rel * ($#{$opt{palette}} - 1) + 1 ];
355 my $indicator = $opt{indicators} && $opt{indicators}->[
356 !length($val) || !$#{$opt{indicators}} ? 0 : # blank
357 $#{$opt{indicators}} < 2 ? 1 :
358 $val >= $order[0] ? -1 :
359 $rel * ($#{$opt{indicators}} - 1e-14) + 1
363 say '' if $opt{width} and $nr and $nr % $opt{width} == 0;
364 print color($color), $_ for $indicator;
367 print $indicator if defined $indicator;
370 $val = sprintf("%*s", $lenval,
371 $opt{reformat} ? $opt{'value-format'}->($val) : $val
373 color($color) for $val;
375 my $line = $lines[$nr] =~ s/\n/$val/r;
376 if (not length $val) {
380 printf '%-*s', $len + length($val), $line;
381 if ($rel and $size) {
382 print $barmark[$_] // $opt{'graph-format'}
383 for 1 .. $rel * $size + .5;
390 say $opt{palette} ? color(0) : '' if $opt{spark};
400 my $linemin = !$opt{hidemin} ? 0 :
401 ($vars{start} = $opt{hidemin}->($#lines));
402 my $linemax = !$opt{hidemax} ? $#lines :
403 ($vars{end} = $opt{hidemax}->($#lines, $vars{start}));
405 $vars{partsum} = sum(0, grep {length} @values[$linemin .. $linemax])
406 if $linemin <= $linemax and ($opt{hidemin} or $opt{hidemax});
412 $vars{avg} = $vars{sum} / @order;
414 say varfmt($opt{report}, \%vars);
419 my ($fmt, $vars) = @_;
420 $fmt =~ s[\$\{ \h*+ ((?: [^{}]++ | \{(?1)\} )+) \}]{
421 my ($name, $op, $cmd) = split /\s*([;:])/, $1, 2;
422 my $format = $name =~ s/\+// || $name !~ s/\#// && $opt{reformat};
423 local $_ = $vars->{$name};
425 $_ = $opt{'value-format'}->($_) if $format;
426 if ($cmd and $op eq ':') {
427 $_ = varfmt($cmd, $vars);
431 warn "Error in \$$name report: $@" if $@;
441 show_stat() if $opt{stat};
442 exit 130 if @_; # 0x80+signo
450 barcat [OPTIONS] [FILES|NUMBERS] (=•.•=)
453 -a, --[no-]ascii Restrict user interface to ASCII characters
454 -C, --[no-]color Force colored output of values and bar markers
455 -c, --count Omit repetitions and count the number of
457 -f, --field=([+]N|REGEXP)
458 Compare values after a given number of whitespace
460 --header Prepend a chart axis with minimum and maximum
462 -H, --human-readable Format values using SI unit prefixes
463 --sexagesimal Convert seconds to HH:MM:SS time format
464 -t, --interval[=(N|-LINES)]
465 Output partial progress every given number of
466 seconds or input lines
467 -l, --length=[-]SIZE[%] Trim line contents (between number and bars)
468 -L, --limit=[N|[-]START(-[END]|+N)]
469 Select a range of lines to display
470 -e, --log Logarithmic (exponential) scale instead of linear
471 --graph-format=CHAR Glyph to repeat for the graph line
472 -m, --markers=FORMAT Statistical positions to indicate on bars
473 --min=N, --max=N Bars extend from 0 or the minimum value if lower
474 --palette=(PRESET|COLORS)
475 Override colors of parsed numbers
476 -_, --spark Replace lines by sparklines
477 --indicators[=CHARS] Prefix a unicode character corresponding to each
479 -s, --stat Total statistics after all data
480 -u, --unmodified Do not reformat values, keeping leading whitespace
481 --value-length=SIZE Reserved space for numbers
482 -w, --width=COLUMNS Override the maximum number of columns to use
483 -h, --usage Overview of available options
484 --help Full pod documentation
485 -V, --version Version information
491 barcat - concatenate texts with graph to visualize values
495 B<barcat> [I<options>] [I<file>... | I<numbers>]
499 Visualizes relative sizes of values read from input
500 (parameters, file(s) or STDIN).
501 Contents are concatenated similar to I<cat>,
502 but numbers are reformatted and a bar graph is appended to each line.
504 Don't worry, barcat does not drink and divide.
505 It can has various options for input and output (re)formatting,
506 but remains limited to one-dimensional charts.
507 For more complex graphing needs
508 you'll need a larger animal like I<gnuplot>.
514 =item B<-a>, B<-->[B<no->]B<ascii>
516 Restrict user interface to ASCII characters,
517 replacing default UTF-8 by their closest approximation.
518 Input is always interpreted as UTF-8 and shown as is.
520 =item B<-C>, B<-->[B<no->]B<color>
522 Force colored output of values and bar markers.
523 Defaults on if output is a tty,
524 disabled otherwise such as when piped or redirected.
525 Can also be disabled by setting B<-M>
526 or the I<NO_COLOR> environment variable.
528 =item B<-c>, B<--count>
530 Omit repetitions and count the number of occurrences.
531 Similar to piping input to C<sort | uniq -c>
532 but keeping the order of first appearances.
534 =item B<-f>, B<--field>=([B<+>]I<number> | I<regexp>)
536 Compare values after a given number of whitespace separators,
537 or matching a regular expression.
539 Unspecified or B<-f0> means values are at the start of each line.
540 With B<-f1> the second word is taken instead.
541 A string can indicate the starting position of a value
542 (such as B<-f:> if preceded by colons),
543 or capture the numbers itself,
544 for example B<-f'(\d+)'> for the first digits anywhere.
545 A shorthand for this is C<+0>, or C<+N> to find the Nth number.
549 Prepend a chart axis with minimum and maximum values labeled.
551 =item B<-H>, B<--human-readable>
553 Format values using SI unit prefixes,
554 turning long numbers like C<12356789> into C<12.4M>.
555 Also changes an exponent C<1.602176634e-19> to C<160.2z>.
556 Short integers are aligned but kept without decimal point.
558 =item B<--sexagesimal>
560 Convert seconds to HH:MM:SS time format.
562 =item B<-t>, B<--interval>[=(I<seconds> | B<->I<lines>)]
564 Output partial progress every given number of seconds or input lines.
565 An update can also be forced by sending a I<SIGALRM> alarm signal.
567 =item B<-l>, B<--length>=[B<->]I<size>[B<%>]
569 Trim line contents (between number and bars)
570 to a maximum number of characters.
571 The exceeding part is replaced by an abbreviation sign,
572 unless B<--length=0>.
574 Prepend a dash (i.e. make negative) to enforce padding
575 regardless of encountered contents.
577 =item B<-L>, B<--limit>=[I<count> | [B<->]I<start>(B<->[I<end>] | B<+>I<count>)]
579 Select a range of lines to display.
580 A single integer indicates the last line number (like I<head>),
581 or first line counting from the bottom if negative (like I<tail>).
583 A range consists of a starting line number followed by either
584 a dash C<-> to an optional end, or plus sign C<+> with count.
586 All hidden input is still counted and analyzed for statistics,
587 but disregarded for padding and bar size.
589 =item B<-e>, B<--log>
591 Logarithmic (B<e>xponential) scale instead of linear
592 to compare orders of magnitude.
594 =item B<--graph-format>=I<character>
596 Glyph to repeat for the graph line.
597 Defaults to a dash C<->.
599 =item B<-m>, B<--markers>=I<format>
601 Statistical positions to indicate on bars.
602 A single indicator glyph precedes each position:
608 Exact value to match on the axis.
609 A vertical bar at the zero crossing is displayed by C<|0>
611 For example C<π3.14> would locate pi.
613 =item B</>I<interval>
615 Repeated at every multiple of a number.
616 For example C<:/1> for a grid at every integer.
618 =item I<percentage>B<v>
620 Ranked value at the given percentile.
621 The default shows C<+> at C<50v> for the mean or median;
622 the middle value or average between middle values.
623 One standard deviation right of the mean is at about C<68.3v>.
624 The default includes C<< >31.73v <68.27v >>
625 to encompass all I<normal> results, or 68% of all entries, by I<< <--> >>.
630 the sum of all values divided by the number of counted lines.
631 Indicated by default as C<=>.
635 =item B<--min>=I<number>, B<--max>=I<number>
637 Bars extend from 0 or the minimum value if lower,
638 to the largest value encountered.
639 These options can be set to customize this range.
641 =item B<--palette>=(I<preset> | I<color>...)
643 Override colors of parsed numbers.
644 Can be any CSI escape, such as C<90> for default dark gray,
645 or alternatively C<1;30> for bright black.
647 In case of additional colors,
648 the last is used for values equal to the maximum, the first for minima.
649 If unspecified, these are green and red respectively (C<31 90 32>).
650 Multiple intermediate colors will be distributed
651 relative to the size of values.
653 A non-numeric name can refer to a predefined color scheme:
659 Minimal set of monochrome brightnesses.
663 Utilize the 24 grayscale ramp in 256-color terminals.
667 Gradient red to white in 7 out of 16 colors.
671 Extended to 17 colors out of 256.
675 Saturated red to green to blue to red.
679 All 215 extended colors in unrelated orders.
683 =item B<-_>, B<--spark>
685 Replace lines by I<sparklines>,
686 single characters (configured by B<--indicators>)
687 corresponding to input values.
689 =item B<--indicators>[=I<characters>]
691 Prefix a unicode character corresponding to each value.
692 The first specified character will be used for non-values,
693 the remaining sequence will be distributed over the range of values.
694 Unspecified, block fill glyphs U+2581-2588 will be used.
696 =item B<-s>, B<--stat>
698 Total statistics after all data.
700 While processing (possibly a neverending pipe),
701 intermediate results are also shown on signal I<SIGINFO> if available (control+t on BSDs)
702 or I<SIGQUIT> otherwise (ctrl+\ on linux).
704 =item B<-u>, B<--unmodified>
706 Do not reformat values, keeping leading whitespace.
707 Keep original value alignment, which may be significant in some programs.
709 =item B<--value-length>=I<size>
711 Reserved space for numbers.
713 =item B<-w>, B<--width>=I<columns>
715 Override the maximum number of columns to use.
716 Appended graphics will extend to fill up the entire screen,
717 otherwise determined by the environment variable I<COLUMNS>
718 or by running the I<tput> command.
720 =item B<-h>, B<--usage>
722 Overview of available options.
726 Full pod documentation
727 as rendered by perldoc.
729 =item B<-V>, B<--version>
739 seq 30 | awk '{print sin($1/10)}' | barcat
741 Compare file sizes (with human-readable numbers):
743 du -d0 -b * | barcat -H
745 Same from formatted results, selecting the first numeric value:
747 tree -s --noreport | barcat -H -f+
749 Compare media metadata, like image size or play time:
751 exiftool -T -p '$megapixels ($imagesize) $filename' * | barcat
753 exiftool -T -p '$duration# $avgbitrate# $filename' * | barcat --sexagesimal
755 find -type f -print0 | xargs -0 -L1 \
756 ffprobe -show_format -of json -v error |
757 jq -r '.format|.duration+" "+.bit_rate+" "+.filename' | barcat --sex
759 Memory usage of user processes with long names truncated:
761 ps xo rss,pid,cmd | barcat -l40
763 Monitor network latency from prefixed results:
765 ping google.com | barcat -f'time=\K' -t
767 Commonly used after counting, eg letter frequencies in text files:
769 cat /usr/share/games/fortunes/*.u8 |
770 perl -CS -nE 'say for grep length, split /\PL*/, uc' |
771 sort | uniq -c | barcat
773 Users on the current server while preserving order:
775 users | tr ' ' '\n' | barcat -c
777 Number of HTTP requests per day:
779 cat httpd/access.log | cut -d\ -f4 | cut -d: -f1 | uniq -c | barcat
781 Any kind of database query results, preserving returned alignment:
783 echo 'SELECT sin(value * .1) FROM generate_series(0, 30) value' |
786 In PostgreSQL from within the client; a fancy C<\dt+> perhaps:
788 > SELECT schemaname, relname, pg_total_relation_size(relid)
789 FROM pg_statio_user_tables ORDER BY idx_blks_hit
792 Same thing in SQLite (requires the sqlite3 client):
795 > SELECT name, sum(pgsize) FROM dbstat GROUP BY 1;
797 Earthquakes worldwide magnitude 1+ in the last 24 hours:
799 curl https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/1.0_day.csv |
800 column -ts, -n | barcat -f4 -u -l80%
802 External datasets, like movies per year:
804 curl https://github.com/prust/wikipedia-movie-data/raw/master/movies.json -L |
805 jq .[].year | uniq -c | barcat
807 Pokémon height comparison:
809 curl https://github.com/Biuni/PokemonGO-Pokedex/raw/master/pokedex.json -L |
810 jq -r '.pokemon[] | [.height,.num,.name] | join(" ")' | barcat
812 USD/EUR exchange rate from CSV provided by the ECB:
814 curl https://sdw.ecb.europa.eu/export.do \
815 -Gd 'node=SEARCHRESULTS&q=EXR.D.USD.EUR.SP00.A&exportType=csv' |
816 barcat -f',\K' --value-length=7
818 Total population history in XML from the World Bank:
820 curl http://api.worldbank.org/v2/country/1W/indicator/SP.POP.TOTL |
821 xmlstarlet sel -t -m '*/*' -v wb:date -o ' ' -v wb:value -n |
822 barcat -f1 -H --markers=+/1e9
824 Population and other information for all countries:
826 curl http://download.geonames.org/export/dump/countryInfo.txt |
827 grep -v '^#\s' | column -ts$'\t' -n | barcat -f+2 -e -u -l150 -s
829 And of course various Git statistics, such commit count by year:
831 git log --pretty=%ci | cut -b-4 | uniq -c | barcat
833 Or the top 3 most frequent authors with statistics over all:
835 git shortlog -sn | barcat -L3 -s
837 Activity graph of the last days (substitute date C<-v-{}d> on BSD):
839 ( git log --pretty=%ci --since=30day | cut -b-10
840 seq 0 30 | xargs -i date +%F -d-{}day ) |
841 sort | uniq -c | awk '$1--' | barcat --spark
843 Sparkline graphics of simple input given as inline parameters:
845 barcat -_ 3 1 4 1 5 0 9 2 4
847 Misusing the spark functionality to draw a lolcat line:
849 seq $(tput cols) | barcat --spark --indicator=- --palette=rainbow
853 Mischa POSLAWSKY <perl@shiar.org>