1

I have a file with a number of lines in a file filename.

I want to count how many lines start with character 'a', with 'b' and so on in one go.

What command i should execute.?

2

Try this:

<file.txt sed 's/^\(.\).*/\1/' | sort | uniq -c

Or, if you want it case insensitive, this:

<file.txt sed 's/^\(.\).*/\1/' | tr a-z A-Z | sort | uniq -c
8

For single character letters:

< file cut -c1 | grep '[[:alpha:]]' | LC_ALL=C sort | LC_ALL=C uniq -c | sort -k 2

To handle combining characters, if in an utf-8 locale:

< file PERLIO=:utf8 perl -Mlocale -MUnicode::Normalize -lne '
  $_=NFKD($_); $n{$&}++ if /^[[:alpha:]]/u && /^\X/u;
  END{for $i (sort keys %n) {print "$n{$i} $i"}}'

(replace $n{$&} with $n{lc$&} for case-independant counting).

On an input like:

fix
été
-dash-
éléphant
παράλληλα
молчит
alphabet
3com
foo
ɪ-letter
ʃ-letter

In my locale, the first one would output:

  1 ɪ
  1 ʃ
  1 a
  1 e
  1 é
  2 f
  1 π
  1 м

Because in éléphant above (which by the way my version of firefox displays incorrectly as it puts the accent on the l), the first é is written as the two unicode characters e and \U0301 (combining acute accent), while in été, it's the \U00E9 precomposed e with acute accent.

And the second one would output:

1 ɪ
1 ʃ
1 a
2 é
2 f
1 π
1 м

(there, all the variants of é have been converted to e\U0301 (the normalised decomposed version)).

While cut -c 1 | grep '[[:alpha:]]' | sort | uniq -c would output:

  2 ɪ
  1 a
  1 e
  1 é
  2 f
  1 π
  1 м

because in my locale, the sorting order of ɪ and ʃ is not defined, so they sort the same and count as the same as far as sort and uniq are concerned.

(note that you need a POSIX cut above. My version of GNU cut is not as it treats characters as bytes, so I had to use the cut built in ksh93 for that).

If the data is only US-ASCII, you can simplify it to:

(export LC_ALL=C; < file cut -c 1 | grep '[[:alpha:]]' | sort | uniq -c)

Or if you want to report 0 for any of the 52 US ASCII letters that are not found:

< file LC_ALL=C awk '{n[substr($0,1,1)]++};END{
  for(i=65;i<=122;i++) if (i < 91 || i > 96) {
    c=sprintf("%c",i);print 0+n[c], c}}'
  • Ok, I quite like that answer, damn you! – ams Aug 30 '13 at 8:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.