A solução de Steven é boa, simples. Não é tão eficaz para arquivos muito grandes (arquivos que não cabem confortavelmente em cerca de metade da sua RAM) por causa da etapa de classificação. Aqui está uma versão do awk. Também é um pouco mais complicado porque tenta fazer a coisa certa para alguns caracteres especiais (novas linhas, '
, \
, :
).
awk '
{for (i=1; i<=length; i++) ++c[substr($0,i,1)]; ++c[RS]}
function chr (x) {return x=="\n" ? "\n" : x==":" ? "\072" :
x=="\" || x=="'\''" ? "\" x : x}
END {for (x in c) printf "'\''%s'\'': %d\n", chr(x), c[x]}
' | sort -t : -k 2 -r | sed 's/\072/:/'
Aqui está uma solução Perl no mesmo princípio. O Perl tem a vantagem de poder ordenar internamente. Além disso, isso não contará corretamente uma nova linha extra se o arquivo não terminar em um caractere de nova linha.
perl -ne '
++$c{$_} foreach split //;
END { printf "'\''%s'\'': %d\n", /[\'\'']/ ? "\$_" : /./ ? $_ : "\n", $c{$_}
foreach (sort {$c{$b} <=> $c{$a}} keys %c) }'