t/examples: skip downloads unless enabled
[barcat.git] / barcat
diff --git a/barcat b/barcat
index c0292257676ade42c1a2b5c7a9d429d092bd92f7..e3d291b32e3b5ce5e46450a2df0372d20a5f5bb5 100755 (executable)
--- a/barcat
+++ b/barcat
@@ -19,10 +19,16 @@ GetOptions(\%opt,
        'field|f=s' => sub {
                eval {
                        local $_ = $_[1];
-                       $opt{anchor} = /\A[0-9]+\z/ ? qr/(?:\S*\h+){$_}\K/ : qr/$_/;
+                       s/\A[0-9]+\z/(?:\\S*\\h+){$_}\\K/;
+                       s{\A[+]([0-9]*)\z}{
+                               (!!$1 && '(?:\d+\D+\b){'.$1.'}\K') . '\s* (?=\d)'
+                       }e;
+                       $opt{anchor} = qr/$_/;
                } or die $@ =~ s/(?:\ at\ \N+)?\Z/ for option $_[0]/r;
        },
        'human-readable|H!',
+       'sexagesimal!',
+       'reformat!',
        'interval|t:i',
        'trim|length|l=s' => sub {
                my ($optname, $optval) = @_;
@@ -111,7 +117,7 @@ $opt{trim}   *= $opt{width} / 100 if $opt{trimpct};
 $opt{units}   = [split //, ' kMGTPEZYyzafpn'.($opt{ascii} ? 'u' : 'μ').'m']
        if $opt{'human-readable'};
 $opt{anchor} //= qr/\A/;
-$opt{'value-length'} = 6 if $opt{units};
+$opt{'value-length'} = 4 if $opt{units};
 $opt{'value-length'} = 1 if $opt{unmodified};
 $opt{'signal-stat'} //= exists $SIG{INFO} ? 'INFO' : 'QUIT';
 $opt{markers} //= '=avg >31.73v <68.27v +50v |0';
@@ -123,9 +129,11 @@ $opt{hidemin} = ($opt{hidemin} || 1) - 1;
 $opt{input} = (@ARGV && $ARGV[0] =~ m/\A[-0-9]/) ? \@ARGV : undef
        and undef $opt{interval};
 
-$opt{'sum-format'} = sub { sprintf '%.8g', $_[0] };
 $opt{'calc-format'} = sub { sprintf '%*.*f', 0, 2, $_[0] };
-$opt{'value-format'} = $opt{units} && sub {
+$opt{'value-format'} = $opt{sexagesimal} ? sub {
+       my $s = abs($_[0]) + .5;
+       sprintf('%s%d:%02d:%02d', $_[0] < 0 && '-', $s/3600, $s/60%60, $s%60);
+} : $opt{units} && sub {
        my $unit = (
                log(abs $_[0] || 1) / log(10)
                - 3 * (abs($_[0]) < .9995)   # shift to smaller unit if below 1
@@ -142,7 +150,8 @@ $opt{'value-format'} = $opt{units} && sub {
                $#{$opt{units}} * 1.5 < abs $unit ? sprintf('e%d', $unit) :
                        $opt{units}->[$unit/3]  # suffix
        );
-};
+} and $opt{reformat}++;
+$opt{'value-format'} ||= sub { sprintf '%.8g', $_[0] };
 
 
 my (@lines, @values, @order);
@@ -189,10 +198,6 @@ while (defined ($_ = $opt{input} ? shift @{ $opt{input} } : readline)) {
                and $. % $opt{interval} == 0;
 }
 
-if ($opt{'zero-missing'}) {
-       push @values, (0) x 10;
-}
-
 $SIG{INT} = 'DEFAULT';
 
 sub color {
@@ -305,8 +310,9 @@ while ($nr <= $limit) {
        print $indicator if defined $indicator;
 
        if (length $val) {
-               $val = $opt{'value-format'} ? $opt{'value-format'}->($val) :
-                       sprintf "%*s", $lenval, $val;
+               $val = sprintf("%*s", $lenval,
+                       $opt{reformat} ? $opt{'value-format'}->($val) : $val
+               );
                color($color) for $val;
        }
        my $line = $lines[$nr] =~ s/\n/$val/r;
@@ -335,19 +341,19 @@ sub show_stat {
                        $linemin += @lines;
                        $linemax = @lines - $linemax;
                }
-               printf '%.8g of ', $opt{'sum-format'}->(
+               printf '%.8g of ', $opt{'value-format'}->(
                        sum(grep {length} @values[$linemin .. $linemax]) // 0
                );
        }
        if (@order) {
                my $total = sum @order;
-               printf '%s total', color(1) . $opt{'sum-format'}->($total) . color(0);
+               printf '%s total', color(1) . $opt{'value-format'}->($total) . color(0);
                printf ' in %d values', scalar @order;
                printf ' over %d lines', scalar @lines if @order != @lines;
                printf(' (%s min, %s avg, %s max)',
-                       color(31) . ($opt{'value-format'} || sub {$_[0]})->($order[-1]) . color(0),
-                       color(36) . ($opt{'value-format'} || $opt{'calc-format'})->($total / @order) . color(0),
-                       color(32) . ($opt{'value-format'} || sub {$_[0]})->($order[0]) . color(0),
+                       color(31) . ($opt{reformat} ? $opt{'value-format'} : sub {$_[0]})->($order[-1]) . color(0),
+                       color(36) . ($opt{reformat} ? $opt{'value-format'} : $opt{'calc-format'})->($total / @order) . color(0),
+                       color(32) . ($opt{reformat} ? $opt{'value-format'} : sub {$_[0]})->($order[0]) . color(0),
                );
        }
        say '';
@@ -370,11 +376,13 @@ Usage:                                               /\_/\
 Options:
   -a, --[no-]ascii         Restrict user interface to ASCII characters
   -C, --[no-]color         Force colored output of values and bar markers
-  -f, --field=(N|REGEXP)   Compare values after a given number of whitespace
+  -f, --field=([+]N|REGEXP)
+                           Compare values after a given number of whitespace
                            separators
       --header             Prepend a chart axis with minimum and maximum
                            values labeled
   -H, --human-readable     Format values using SI unit prefixes
+      --sexagesimal        Convert seconds to HH:MM:SS time format
   -t, --interval[=(N|-LINES)]
                            Output partial progress every given number of
                            seconds or input lines
@@ -438,7 +446,7 @@ disabled otherwise such as when piped or redirected.
 Can also be disabled by setting I<-M>
 or the I<NO_COLOR> environment variable.
 
-=item -f, --field=(<number> | <regexp>)
+=item -f, --field=([+]<number> | <regexp>)
 
 Compare values after a given number of whitespace separators,
 or matching a regular expression.
@@ -449,6 +457,7 @@ A string can indicate the starting position of a value
 (such as I<-f:> if preceded by colons),
 or capture the numbers itself,
 for example I<-f'(\d+)'> for the first digits anywhere.
+A shorthand for this is I<+0>, or I<+N> to find the Nth number.
 
 =item --header
 
@@ -461,6 +470,10 @@ turning long numbers like I<12356789> into I<12.4M>.
 Also changes an exponent I<1.602176634e-19> to I<160.2z>.
 Short integers are aligned but kept without decimal point.
 
+=item --sexagesimal
+
+Convert seconds to HH:MM:SS time format.
+
 =item -t, --interval[=(<seconds> | -<lines>)]
 
 Output partial progress every given number of seconds or input lines.
@@ -599,6 +612,20 @@ Compare file sizes (with human-readable numbers):
 
     du -d0 -b * | barcat -H
 
+Same from formatted results, selecting the first numeric value:
+
+    tree -s --noreport | barcat -H -f+
+
+Compare media metadata, like image size or play time:
+
+    exiftool -T -p '$megapixels ($imagesize) $filename' * | barcat
+
+    exiftool -T -p '$duration# $avgbitrate# $filename' * | barcat --sexagesimal
+
+    find -type f -print0 | xargs -0 -L1 \
+    ffprobe -show_format -of json -v error |
+    jq -r '.format|.duration+" "+.bit_rate+" "+.filename' | barcat --sex
+
 Memory usage of user processes with long names truncated:
 
     ps xo rss,pid,cmd | barcat -l40
@@ -657,6 +684,11 @@ Total population history in XML from the World Bank:
     xmlstarlet sel -t -m '*/*' -v wb:date -o ' ' -v wb:value -n |
     barcat -f1 -H
 
+Population and other information for all countries:
+
+    curl http://download.geonames.org/export/dump/countryInfo.txt |
+    grep -v '^#\s' | column -tns$'\t' | barcat -f+2 -u -l150 -s
+
 And of course various Git statistics, such commit count by year:
 
     git log --pretty=%ci | cut -b-4 | uniq -c | barcat