count duplicates anchored by specified field
[barcat.git] / barcat
diff --git a/barcat b/barcat
index fe8c11dffa26a5361c78a22ad6e3b662dc8ff997..9a19d02a2c141529c3ddb5cbd875e9027649986c 100755 (executable)
--- a/barcat
+++ b/barcat
@@ -26,6 +26,7 @@ GetOptions(\%opt,
                        $opt{anchor} = qr/$_/;
                } or die $@ =~ s/(?:\ at\ \N+)?\Z/ for option $_[0]/r;
        },
+       'count|c!',
        'human-readable|H!',
        'sexagesimal!',
        'reformat!',
@@ -94,7 +95,7 @@ GetOptions(\%opt,
                                202 208 214 220 226  227 228 229 230 231  159
                        )],
                        whites => [qw( 1;30 0;37 1;37 )],
-                       greys  => [map {"38;5;$_"} 0, 232..255, 15],
+                       grays  => [map {"38;5;$_"} 0, 232..255, 15],
                        random => [map {"38;5;$_"} List::Util::shuffle(17..231)],
                        rainbow=> [map {"38;5;$_"}
                                196, # r
@@ -189,7 +190,7 @@ $opt{'value-format'} = $opt{sexagesimal} ? sub {
 $opt{'value-format'} ||= sub { sprintf '%.8g', $_[0] };
 
 
-my (@lines, @values, @order);
+my (@lines, @values, @order, %uniq);
 
 $SIG{$_} = \&show_stat for $opt{'signal-stat'} || ();
 $SIG{ALRM} = sub {
@@ -209,9 +210,17 @@ if (defined $opt{interval}) {
 }
 
 my $float = qr<[0-9]* [.]? [0-9]+ (?: e[+-]?[0-9]+ )?>; # positive numberish
-my $valmatch = qr< $opt{anchor} ( \h* -? $float |) >x;
+my $valmatch = qr< $opt{anchor} ( \h* -? $float |) >;
 while (defined ($_ = $opt{input} ? shift @{ $opt{input} } : readline)) {
        s/\r?\n\z//;
+       if ($opt{count}) {
+               my ($valnum) = m/$opt{anchor} (\S*)/;
+               $valnum //= '';
+               $uniq{$valnum}++ and next;
+               push @lines, "\n " . $_;
+               push @values, $valnum;
+               next;
+       }
        s/\A\h*// unless $opt{unmodified};
        my $valnum = s/$valmatch/\n/ && $1;
        push @values, $valnum;
@@ -247,6 +256,11 @@ state $nr = $opt{hidemin} ? $opt{hidemin}->($#lines) : 0;
 
 my $limit = $opt{hidemax} ? $opt{hidemax}->($#lines, $nr) : $#lines;
 
+if ($opt{count}) {
+       $_ = $uniq{$_} for @values;
+       @order = @values;
+}
+
 @order = sort { $b <=> $a } @order unless tied @order;
 my $maxval = $opt{maxval} // (
        $opt{hidemax} ? max grep { length } @values[$nr .. $limit] :
@@ -438,6 +452,8 @@ Usage:                                               /\_/\
 Options:
   -a, --[no-]ascii         Restrict user interface to ASCII characters
   -C, --[no-]color         Force colored output of values and bar markers
+  -c, --count              Omit repetitions and count the number of
+                           occurrences
   -f, --field=([+]N|REGEXP)
                            Compare values after a given number of whitespace
                            separators
@@ -509,6 +525,12 @@ disabled otherwise such as when piped or redirected.
 Can also be disabled by setting B<-M>
 or the I<NO_COLOR> environment variable.
 
+=item B<-c>, B<--count>
+
+Omit repetitions and count the number of occurrences.
+Similar to piping input to C<sort | uniq -c>
+but keeping the order of first appearances.
+
 =item B<-f>, B<--field>=([B<+>]I<number> | I<regexp>)
 
 Compare values after a given number of whitespace separators,
@@ -619,7 +641,7 @@ These options can be set to customize this range.
 =item B<--palette>=(I<preset> | I<color>...)
 
 Override colors of parsed numbers.
-Can be any CSI escape, such as C<90> for default dark grey,
+Can be any CSI escape, such as C<90> for default dark gray,
 or alternatively C<1;30> for bright black.
 
 In case of additional colors,
@@ -628,8 +650,35 @@ If unspecified, these are green and red respectively (C<31 90 32>).
 Multiple intermediate colors will be distributed
 relative to the size of values.
 
-Predefined color schemes are named I<whites> and I<fire>,
-or I<greys> and I<fire256> for 256-color variants.
+A non-numeric name can refer to a predefined color scheme:
+
+=over 8
+
+=item B<whites>
+
+Minimal set of monochrome brightnesses.
+
+=item B<grays>
+
+Utilize the 24 grayscale ramp in 256-color terminals.
+
+=item B<fire>
+
+Gradient red to white in 7 out of 16 colors.
+
+=item B<fire256>
+
+Extended to 17 colors out of 256.
+
+=item B<rainbow>
+
+Saturated red to green to blue to red.
+
+=item B<random>
+
+All 215 extended colors in unrelated orders.
+
+=back
 
 =item B<-_>, B<--spark>
 
@@ -715,19 +764,19 @@ Monitor network latency from prefixed results:
 
     ping google.com | barcat -f'time=\K' -t
 
-Commonly used after counting, for example users on the current server:
-
-    users | tr ' ' '\n' | sort | uniq -c | barcat
-
-Letter frequencies in text files:
+Commonly used after counting, eg letter frequencies in text files:
 
     cat /usr/share/games/fortunes/*.u8 |
     perl -CS -nE 'say for grep length, split /\PL*/, uc' |
     sort | uniq -c | barcat
 
+Users on the current server while preserving order:
+
+    users | tr ' ' '\n' | barcat -c
+
 Number of HTTP requests per day:
 
-    cat httpd/access.log | cut -d\  -f4 | cut -d: -f1 | uniq -c | barcat
+    barcat -cf'\[([^:]+)' httpd/access.log
 
 Any kind of database query results, preserving returned alignment: