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"
213 my $float = qr<[0-9]* [.]? [0-9]+ (?: e[+-]?[0-9]+ )?>; # positive numberish
214 my $valmatch = qr< $opt{anchor} ( \h* -? $float |) >;
215 while (defined ($_ = $opt{input} ? shift @{ $opt{input} } : readline)) {
219 ($valnum) = m/$opt{anchor} (\S*)/;
221 $uniq{$valnum}++ and next;
222 push @values, $valnum;
226 s/\A\h*// unless $opt{unmodified};
227 $valnum = s/$valmatch/\n/ && $1;
228 push @values, $valnum;
229 push @order, $valnum if length $valnum;
232 if (defined $opt{trim} and defined $valnum) {
233 my $trimpos = abs $opt{trim};
234 $trimpos -= length $valnum if $opt{unmodified};
236 $_ = substr $_, 0, 2;
238 elsif (length > $trimpos) {
239 # cut and replace (intentional lvalue for speed, contrary to PBP)
240 substr($_, $trimpos - 1) = $opt{ascii} ? '>' : '…';
246 show_lines() if defined $opt{interval} and $opt{interval} < 0
247 and $. % $opt{interval} == 0;
250 $SIG{INT} = 'DEFAULT';
253 $opt{color} and defined $_[0] or return '';
254 return "\e[$_[0]m" if defined wantarray;
255 $_ = color(@_) . $_ . color(0) if defined;
260 state $nr = $opt{hidemin} ? $opt{hidemin}->($#lines) : 0;
261 @lines > $nr or return;
263 my $limit = $opt{hidemax} ? $opt{hidemax}->($#lines, $nr) : $#lines;
266 $_ = $uniq{$_} for @values[$nr .. $limit];
270 @order = sort { $b <=> $a } @order unless tied @order;
271 my $maxval = $opt{maxval} // (
272 $opt{hidemax} ? max grep { length } @values[$nr .. $limit] :
275 my $minval = $opt{minval} // min $order[-1] // (), 0;
276 my $range = $maxval - $minval;
277 $range &&= log $range if $opt{log};
278 my $lenval = $opt{'value-length'} // max map { length } @order;
279 my $len = defined $opt{trim} && $opt{trim} <= 0 ? -$opt{trim} + 1 :
280 max(map { length $values[$_] && length $lines[$_] } $nr .. $limit)
282 my $size = defined $opt{width} && $range &&
283 ($opt{width} - $lenval - $len - !!$opt{indicators}); # bar multiplication
286 if ($opt{markers} and $size > 0) {
287 for my $markspec (split /\h/, $opt{markers}) {
288 my ($char, $func) = split //, $markspec, 2;
290 if ($func eq 'avg') {
291 return sum(@order) / @order;
293 elsif ($func =~ /\A([0-9.]+)v\z/) {
295 "Invalid marker $char: percentile $1 out of bounds\n"
297 my $index = $#order * $1 / 100;
298 return ($order[$index] + $order[$index + .5]) / 2;
300 elsif ($func =~ /\A-?[0-9.]+\z/) {
303 elsif ($func =~ /\A\/($float)\z/) {
304 my @range = my $multiple = my $next = $1;
305 while ($next < $maxval) {
306 $multiple *= 10 if $opt{log};
307 push @range, $next += $multiple;
312 die "Unknown marker $char: $func\n";
321 $pos &&= log $pos if $opt{log};
323 color(36) for $barmark[$pos / $range * $size] = $char;
327 state $lastmax = $maxval;
328 if ($maxval > $lastmax) {
329 print ' ' x ($lenval + $len);
332 ($lastmax - $minval) * $size / $range + .5,
333 '-' x (($values[$nr - 1] - $minval) * $size / $range);
335 say '+' x (($range - $lastmax) * $size / $range + .5);
342 color(31), sprintf('%*s', $lenval, $minval),
343 color(90), '-', color(36), '+',
344 color(32), sprintf('%*s', $size - 3, $maxval),
345 color(90), '-', color(36), '+',
349 while ($nr <= $limit) {
350 my $val = $values[$nr];
353 $rel = $val - $minval;
354 $rel &&= log $rel if $opt{log};
355 $rel = min(1, $rel / $range) if $range; # 0..1
357 my $color = !length $val || !$opt{palette} ? undef :
358 $val == $order[0] ? $opt{palette}->[-1] : # max
359 $val == $order[-1] ? $opt{palette}->[0] : # min
360 $opt{palette}->[ $rel * ($#{$opt{palette}} - 1) + 1 ];
361 my $indicator = $opt{indicators} && $opt{indicators}->[
362 !length($val) || !$#{$opt{indicators}} ? 0 : # blank
363 $#{$opt{indicators}} < 2 ? 1 :
364 $val >= $order[0] ? -1 :
365 $rel * ($#{$opt{indicators}} - 1e-14) + 1
369 say '' if $opt{width} and $nr and $nr % $opt{width} == 0;
370 print color($color), $_ for $indicator;
373 print $indicator if defined $indicator;
376 $val = sprintf("%*s", $lenval,
377 $opt{reformat} ? $opt{'value-format'}->($val) : $val
379 color($color) for $val;
381 my $line = $lines[$nr] =~ s/\n/$val/r;
382 if (not length $val) {
386 printf '%-*s', $len + length($val), $line;
387 if ($rel and $size) {
388 print $barmark[$_] // $opt{'graph-format'}
389 for 1 .. $rel * $size + .5;
396 say $opt{palette} ? color(0) : '' if $opt{spark};
397 %uniq = () if $opt{interval} and $opt{count};
407 my $linemin = !$opt{hidemin} ? 0 :
408 ($vars{start} = $opt{hidemin}->($#lines));
409 my $linemax = !$opt{hidemax} ? $#lines :
410 ($vars{end} = $opt{hidemax}->($#lines, $vars{start}));
412 $vars{partsum} = sum(0, grep {length} @values[$linemin .. $linemax])
413 if $linemin <= $linemax and ($opt{hidemin} or $opt{hidemax});
419 $vars{avg} = $vars{sum} / @order;
421 say varfmt($opt{report}, \%vars);
426 my ($fmt, $vars) = @_;
427 $fmt =~ s[\$\{ \h*+ ((?: [^{}]++ | \{(?1)\} )+) \}]{
428 my ($name, $op, $cmd) = split /\s*([;:])/, $1, 2;
429 my $format = $name =~ s/\+// || $name !~ s/\#// && $opt{reformat};
430 local $_ = $vars->{$name};
432 $_ = $opt{'value-format'}->($_) if $format;
433 if ($cmd and $op eq ':') {
434 $_ = varfmt($cmd, $vars);
438 warn "Error in \$$name report: $@" if $@;
448 show_stat() if $opt{stat};
449 exit 130 if @_; # 0x80+signo
457 barcat [OPTIONS] [FILES|NUMBERS] (=•.•=)
460 -a, --[no-]ascii Restrict user interface to ASCII characters
461 -C, --[no-]color Force colored output of values and bar markers
462 -c, --count Omit repetitions and count the number of
464 -f, --field=([+]N|REGEXP)
465 Compare values after a given number of whitespace
467 --header Prepend a chart axis with minimum and maximum
469 -H, --human-readable Format values using SI unit prefixes
470 --sexagesimal Convert seconds to HH:MM:SS time format
471 -t, --interval[=(N|-LINES)]
472 Output partial progress every given number of
473 seconds or input lines
474 -l, --length=[-]SIZE[%] Trim line contents (between number and bars)
475 -L, --limit=[N|[-]START(-[END]|+N)]
476 Select a range of lines to display
477 -e, --log Logarithmic (exponential) scale instead of linear
478 --graph-format=CHAR Glyph to repeat for the graph line
479 -m, --markers=FORMAT Statistical positions to indicate on bars
480 --min=N, --max=N Bars extend from 0 or the minimum value if lower
481 --palette=(PRESET|COLORS)
482 Override colors of parsed numbers
483 -_, --spark Replace lines by sparklines
484 --indicators[=CHARS] Prefix a unicode character corresponding to each
486 -s, --stat Total statistics after all data
487 -u, --unmodified Do not reformat values, keeping leading whitespace
488 --value-length=SIZE Reserved space for numbers
489 -w, --width=COLUMNS Override the maximum number of columns to use
490 -h, --usage Overview of available options
491 --help Full pod documentation
492 -V, --version Version information
498 barcat - concatenate texts with graph to visualize values
502 B<barcat> [I<options>] [I<file>... | I<numbers>]
506 Visualizes relative sizes of values read from input
507 (parameters, file(s) or STDIN).
508 Contents are concatenated similar to I<cat>,
509 but numbers are reformatted and a bar graph is appended to each line.
511 Don't worry, barcat does not drink and divide.
512 It can has various options for input and output (re)formatting,
513 but remains limited to one-dimensional charts.
514 For more complex graphing needs
515 you'll need a larger animal like I<gnuplot>.
521 =item B<-a>, B<-->[B<no->]B<ascii>
523 Restrict user interface to ASCII characters,
524 replacing default UTF-8 by their closest approximation.
525 Input is always interpreted as UTF-8 and shown as is.
527 =item B<-C>, B<-->[B<no->]B<color>
529 Force colored output of values and bar markers.
530 Defaults on if output is a tty,
531 disabled otherwise such as when piped or redirected.
532 Can also be disabled by setting B<-M>
533 or the I<NO_COLOR> environment variable.
535 =item B<-c>, B<--count>
537 Omit repetitions and count the number of occurrences.
538 Similar to piping input to C<sort | uniq -c>
539 but keeping the order of first appearances.
541 =item B<-f>, B<--field>=([B<+>]I<number> | I<regexp>)
543 Compare values after a given number of whitespace separators,
544 or matching a regular expression.
546 Unspecified or B<-f0> means values are at the start of each line.
547 With B<-f1> the second word is taken instead.
548 A string can indicate the starting position of a value
549 (such as B<-f:> if preceded by colons),
550 or capture the numbers itself,
551 for example B<-f'(\d+)'> for the first digits anywhere.
552 A shorthand for this is C<+0>, or C<+N> to find the Nth number.
556 Prepend a chart axis with minimum and maximum values labeled.
558 =item B<-H>, B<--human-readable>
560 Format values using SI unit prefixes,
561 turning long numbers like C<12356789> into C<12.4M>.
562 Also changes an exponent C<1.602176634e-19> to C<160.2z>.
563 Short integers are aligned but kept without decimal point.
565 =item B<--sexagesimal>
567 Convert seconds to HH:MM:SS time format.
569 =item B<-t>, B<--interval>[=(I<seconds> | B<->I<lines>)]
571 Output partial progress every given number of seconds or input lines.
572 An update can also be forced by sending a I<SIGALRM> alarm signal.
574 =item B<-l>, B<--length>=[B<->]I<size>[B<%>]
576 Trim line contents (between number and bars)
577 to a maximum number of characters.
578 The exceeding part is replaced by an abbreviation sign,
579 unless B<--length=0>.
581 Prepend a dash (i.e. make negative) to enforce padding
582 regardless of encountered contents.
584 =item B<-L>, B<--limit>=[I<count> | [B<->]I<start>(B<->[I<end>] | B<+>I<count>)]
586 Select a range of lines to display.
587 A single integer indicates the last line number (like I<head>),
588 or first line counting from the bottom if negative (like I<tail>).
590 A range consists of a starting line number followed by either
591 a dash C<-> to an optional end, or plus sign C<+> with count.
593 All hidden input is still counted and analyzed for statistics,
594 but disregarded for padding and bar size.
596 =item B<-e>, B<--log>
598 Logarithmic (B<e>xponential) scale instead of linear
599 to compare orders of magnitude.
601 =item B<--graph-format>=I<character>
603 Glyph to repeat for the graph line.
604 Defaults to a dash C<->.
606 =item B<-m>, B<--markers>=I<format>
608 Statistical positions to indicate on bars.
609 A single indicator glyph precedes each position:
615 Exact value to match on the axis.
616 A vertical bar at the zero crossing is displayed by C<|0>
618 For example C<π3.14> would locate pi.
620 =item B</>I<interval>
622 Repeated at every multiple of a number.
623 For example C<:/1> for a grid at every integer.
625 =item I<percentage>B<v>
627 Ranked value at the given percentile.
628 The default shows C<+> at C<50v> for the mean or median;
629 the middle value or average between middle values.
630 One standard deviation right of the mean is at about C<68.3v>.
631 The default includes C<< >31.73v <68.27v >>
632 to encompass all I<normal> results, or 68% of all entries, by I<< <--> >>.
637 the sum of all values divided by the number of counted lines.
638 Indicated by default as C<=>.
642 =item B<--min>=I<number>, B<--max>=I<number>
644 Bars extend from 0 or the minimum value if lower,
645 to the largest value encountered.
646 These options can be set to customize this range.
648 =item B<--palette>=(I<preset> | I<color>...)
650 Override colors of parsed numbers.
651 Can be any CSI escape, such as C<90> for default dark gray,
652 or alternatively C<1;30> for bright black.
654 In case of additional colors,
655 the last is used for values equal to the maximum, the first for minima.
656 If unspecified, these are green and red respectively (C<31 90 32>).
657 Multiple intermediate colors will be distributed
658 relative to the size of values.
660 A non-numeric name can refer to a predefined color scheme:
666 Minimal set of monochrome brightnesses.
670 Utilize the 24 grayscale ramp in 256-color terminals.
674 Gradient red to white in 7 out of 16 colors.
678 Extended to 17 colors out of 256.
682 Saturated red to green to blue to red.
686 All 215 extended colors in unrelated orders.
690 =item B<-_>, B<--spark>
692 Replace lines by I<sparklines>,
693 single characters (configured by B<--indicators>)
694 corresponding to input values.
696 =item B<--indicators>[=I<characters>]
698 Prefix a unicode character corresponding to each value.
699 The first specified character will be used for non-values,
700 the remaining sequence will be distributed over the range of values.
701 Unspecified, block fill glyphs U+2581-2588 will be used.
703 =item B<-s>, B<--stat>
705 Total statistics after all data.
707 While processing (possibly a neverending pipe),
708 intermediate results are also shown on signal I<SIGINFO> if available (control+t on BSDs)
709 or I<SIGQUIT> otherwise (ctrl+\ on linux).
711 =item B<-u>, B<--unmodified>
713 Do not reformat values, keeping leading whitespace.
714 Keep original value alignment, which may be significant in some programs.
716 =item B<--value-length>=I<size>
718 Reserved space for numbers.
720 =item B<-w>, B<--width>=I<columns>
722 Override the maximum number of columns to use.
723 Appended graphics will extend to fill up the entire screen,
724 otherwise determined by the environment variable I<COLUMNS>
725 or by running the I<tput> command.
727 =item B<-h>, B<--usage>
729 Overview of available options.
733 Full pod documentation
734 as rendered by perldoc.
736 =item B<-V>, B<--version>
746 seq 30 | awk '{print sin($1/10)}' | barcat
748 Compare file sizes (with human-readable numbers):
750 du -d0 -b * | barcat -H
752 Same from formatted results, selecting the first numeric value:
754 tree -s --noreport | barcat -H -f+
756 Compare media metadata, like image size or play time:
758 exiftool -T -p '$megapixels ($imagesize) $filename' * | barcat
760 exiftool -T -p '$duration# $avgbitrate# $filename' * | barcat --sexagesimal
762 find -type f -print0 | xargs -0 -L1 \
763 ffprobe -show_format -of json -v error |
764 jq -r '.format|.duration+" "+.bit_rate+" "+.filename' | barcat --sex
766 Memory usage of user processes with long names truncated:
768 ps xo rss,pid,cmd | barcat -l40
770 Monitor network latency from prefixed results:
772 ping google.com | barcat -f'time=\K' -t
774 Commonly used after counting, eg letter frequencies in text files:
776 cat /usr/share/games/fortunes/*.u8 |
777 perl -CS -nE 'say for grep length, split /\PL*/, uc' |
778 sort | uniq -c | barcat
780 Users on the current server while preserving order:
782 users | tr ' ' '\n' | barcat -c
784 Number of HTTP requests per day:
786 barcat -cf'\[([^:]+)' httpd/access.log
788 Any kind of database query results, preserving returned alignment:
790 echo 'SELECT sin(value * .1) FROM generate_series(0, 30) value' |
793 In PostgreSQL from within the client; a fancy C<\dt+> perhaps:
795 > SELECT schemaname, relname, pg_total_relation_size(relid)
796 FROM pg_statio_user_tables ORDER BY idx_blks_hit
799 Same thing in SQLite (requires the sqlite3 client):
802 > SELECT name, sum(pgsize) FROM dbstat GROUP BY 1;
804 Earthquakes worldwide magnitude 1+ in the last 24 hours:
806 curl https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/1.0_day.csv |
807 column -ts, -n | barcat -f4 -u -l80%
809 External datasets, like movies per year:
811 curl https://github.com/prust/wikipedia-movie-data/raw/master/movies.json -L |
812 jq .[].year | uniq -c | barcat
814 Pokémon height comparison:
816 curl https://github.com/Biuni/PokemonGO-Pokedex/raw/master/pokedex.json -L |
817 jq -r '.pokemon[] | [.height,.num,.name] | join(" ")' | barcat
819 USD/EUR exchange rate from CSV provided by the ECB:
821 curl https://sdw.ecb.europa.eu/export.do \
822 -Gd 'node=SEARCHRESULTS&q=EXR.D.USD.EUR.SP00.A&exportType=csv' |
823 barcat -f',\K' --value-length=7
825 Total population history in XML from the World Bank:
827 curl http://api.worldbank.org/v2/country/1W/indicator/SP.POP.TOTL |
828 xmlstarlet sel -t -m '*/*' -v wb:date -o ' ' -v wb:value -n |
829 barcat -f1 -H --markers=+/1e9
831 Population and other information for all countries:
833 curl http://download.geonames.org/export/dump/countryInfo.txt |
834 grep -v '^#\s' | column -ts$'\t' -n | barcat -f+2 -e -u -l150 -s
836 And of course various Git statistics, such commit count by year:
838 git log --pretty=%ci | cut -b-4 | uniq -c | barcat
840 Or the top 3 most frequent authors with statistics over all:
842 git shortlog -sn | barcat -L3 -s
844 Activity graph of the last days (substitute date C<-v-{}d> on BSD):
846 ( git log --pretty=%ci --since=30day | cut -b-10
847 seq 0 30 | xargs -i date +%F -d-{}day ) |
848 sort | uniq -c | awk '$1--' | barcat --spark
850 Sparkline graphics of simple input given as inline parameters:
852 barcat -_ 3 1 4 1 5 0 9 2 4
854 Misusing the spark functionality to draw a lolcat line:
856 seq $(tput cols) | barcat --spark --indicator=- --palette=rainbow
860 Mischa POSLAWSKY <perl@shiar.org>