¿Cuál es la forma más rápida de contar el número de cada personaje en un archivo?

121

Quiero contar los caracteres A's T's C's G's N's y "-" en un archivo, o cada letra si es necesario, ¿hay un comando rápido de Unix para hacer esto?

Kirstin
fuente
56
Conteo de bases en cadenas de ADN?
Indrek
12
Me encanta esta pregunta, hay muchos enfoques y herramientas diferentes para resolver el mismo problema.
Journeyman Geek
10
Je, este es el código límite de golf
Earlz
13
si alguien está interesado en la versión de Windows PowerShell:[System.IO.File]::ReadAllText("C:\yourfile.txt").ToCharArray() | Group-Object $_ | Sort Count -Descending
Guillaume86
44
Ok, creo que encontré la manera pura de PS:Get-Content "C:\eula.3082.txt" | % { $_.ToCharArray() } | Group-Object | Sort Count -Descending
Guillaume86

Respuestas:

136

Si quieres algo de velocidad real:

echo 'int cache[256],x,y;char buf[4096],letters[]="tacgn-"; int main(){while((x=read(0,buf,sizeof buf))>0)for(y=0;y<x;y++)cache[(unsigned char)buf[y]]++;for(x=0;x<sizeof letters-1;x++)printf("%c: %d\n",letters[x],cache[letters[x]]);}' | gcc -w -xc -; ./a.out < file; rm a.out;

Es un pseudo-one-liner increíblemente rápido.

Una prueba simple muestra que en mi Core i7 CPU 870 @ 2.93GHz cuenta con poco más de 600MB / s:

$ du -h bigdna 
1.1G    bigdna

time ./a.out < bigdna 
t: 178977308
a: 178958411
c: 178958823
g: 178947772
n: 178959673
-: 178939837

real    0m1.718s
user    0m1.539s
sys     0m0.171s

A diferencia de las soluciones que involucran la clasificación, esta se ejecuta en memoria constante (4K), lo cual es muy útil si su archivo es mucho más grande que su memoria RAM.

Y, por supuesto, con un poco de grasa en el codo, podemos reducir 0,7 segundos:

echo 'int cache[256],x,buf[4096],*bp,*ep;char letters[]="tacgn-"; int main(){while((ep=buf+(read(0,buf,sizeof buf)/sizeof(int)))>buf)for(bp=buf;bp<ep;bp++){cache[(*bp)&0xff]++;cache[(*bp>>8)&0xff]++;cache[(*bp>>16)&0xff]++;cache[(*bp>>24)&0xff]++;}for(x=0;x<sizeof letters-1;x++)printf("%c: %d\n",letters[x],cache[letters[x]]);}' | gcc -O2 -xc -; ./a.out < file; rm a.out;

Las redes de poco más de 1.1GB / s terminan en:

real    0m0.943s
user    0m0.798s
sys     0m0.134s

A modo de comparación, probé algunas de las otras soluciones en esta página que parecían tener algún tipo de promesa de velocidad.

La solución sed/ awkhizo un valiente esfuerzo, pero murió después de 30 segundos. Con una expresión regular tan simple, espero que esto sea un error en sed (GNU sed versión 4.2.1):

$ time sed 's/./&\n/g' bigdna | awk '!/^$/{a[$0]++}END{for (i in a)print i,a[i];}' 
sed: couldn't re-allocate memory

real    0m31.326s
user    0m21.696s
sys     0m2.111s

El método perl también parecía prometedor, pero me di por vencido después de ejecutarlo durante 7 minutos.

time perl -e 'while (<>) {$c{$&}++ while /./g} print "$c{$_} $_\n" for keys %c' < bigdna 
^C

real    7m44.161s
user    4m53.941s
sys     2m35.593s
Dave
fuente
1
+1 Para una solución sensata cuando hay muchos datos, y no solo un puñado de bytes. Sin embargo, los archivos están en el caché del disco, ¿no?
Daniel Beck
2
Lo bueno es que tiene una complejidad de O (N) en el procesamiento y O (1) en la memoria. Las tuberías generalmente tienen O (N log N) en el procesamiento (o incluso O (N ^ 2)) y O (N) en la memoria.
Martin Ueding
73
Sin embargo, está extendiendo un poco la definición de "línea de comando".
gerrit
11
Flexión épica de los requisitos de la pregunta: lo apruebo; p. superuser.com/a/486037/10165 <- alguien ejecutó puntos de referencia, y esta es la opción más rápida.
Journeyman Geek
2
+1 Aprecio mucho el buen uso de C en los lugares correctos.
Jeff Ferland
119

grep -o foo.text -e A -e T -e C -e G -e N -e -|sort|uniq -c

Hará el truco como un trazador de líneas. Sin embargo, se necesita una pequeña explicación.

grep -o foo.text -e A -e T -e C -e G -e N -e -greps el archivo foo.text para las letras a y gy el carácter -de cada carácter que desea buscar. También lo imprime un carácter por línea.

sortlo ordena en orden. Esto prepara el escenario para la siguiente herramienta

uniq -ccuenta las ocurrencias duplicadas consecutivas de cualquier línea. En este caso, dado que tenemos una lista ordenada de caracteres, obtenemos un recuento ordenado de los caracteres que seleccionamos en el primer paso

Si foo.txt contuviera la cadena, GATTACA-esto es lo que obtendría de este conjunto de comandos

[geek@atremis ~]$ grep -o foo.text -e A -e T -e C -e G -e N -e -|sort|uniq -c
      1 -
      3 A
      1 C
      1 G
      2 T
Journeyman Geek
fuente
8
Bloody unix magic! : D
Pitto
27
si solo hay caracteres CTAG en sus archivos, la expresión regular en sí misma no tiene sentido, ¿verdad? grep -o. El | ordenar | uniq -c funcionaría igual de bien, afaik.
sylvainulg
77
+1 He estado usando grep durante 25 años y no lo sabía -o.
LarsH
99
@JourneymanGeek: El problema con esto es que genera muchos datos que luego se reenvían para su clasificación. Sería más barato dejar que un programa analice cada personaje. Vea la respuesta de Dave para una respuesta de complejidad de memoria O (1) en lugar de O (N).
Martin Ueding
2
@Pitto Las compilaciones nativas de Windows de Coreutils están ampliamente disponibles - solo pregúntele a Google o somesuch
OrangeDog
46

Pruebe este, inspirado en la respuesta de @ Journeyman.

grep -o -E 'A|T|C|G|N|-' foo.txt | sort | uniq -c

La clave es conocer la opción -o para grep . Esto divide la coincidencia, de modo que cada línea de salida corresponde a una sola instancia del patrón, en lugar de la línea completa para cualquier línea que coincida. Dado este conocimiento, todo lo que necesitamos es un patrón para usar y una forma de contar las líneas. Usando una expresión regular, podemos crear un patrón disyuntivo que coincida con cualquiera de los caracteres que menciona:

A|T|C|G|N|-

Esto significa "coincidencia A o T o C o G o N o -". El manual describe varias sintaxis de expresiones regulares que puede usar .

Ahora tenemos una salida que se parece a esto:

$ grep -o -E 'A|T|C|G|N|-' foo.txt 
A
T
C
G
N
-
-
A
A
N
N
N

Nuestro último paso es fusionar y contar todas las líneas similares, que simplemente se pueden lograr con un sort | uniq -c, como en la respuesta de @ Journeyman. El género nos da una salida como esta:

$ grep -o -E 'A|T|C|G|N|-' foo.txt | sort
-
-
A
A
A
C
G
N
N
N
N
T

Lo cual, cuando se canaliza uniq -c, finalmente se parece a lo que queremos:

$ grep -o -E 'A|T|C|G|N|-' foo.txt | sort | uniq -c
      2 -
      3 A
      1 C
      1 G
      4 N
      1 T

Anexo: Si desea totalizar el número de caracteres A, C, G, N, T y - en un archivo, puede canalizar la salida grep en wc -llugar de sort | uniq -c. Hay muchas cosas diferentes que puede contar con solo ligeras modificaciones a este enfoque.

crazy2be
fuente
Realmente necesito profundizar en los rabbitholes que son coreutils y regex. Esto es algo más elegante que el mío; p
Journeyman Geek
2
@JourneymanGeek: Aprender expresiones regulares bien vale la pena, ya que es útil para muchas cosas. Simplemente entienda sus limitaciones, y no abuse del poder al intentar hacer cosas fuera del alcance de las capacidades de expresiones regulares, como tratar de analizar XHTML .
crazy2be
20
grep -o '[ATCGN-]' podría ser un poco más legible aquí.
sylvainulg
14

Una línea que cuenta todas las letras con Python:

$ python -c "import collections, pprint; pprint.pprint(dict(collections.Counter(open('FILENAME_HERE', 'r').read())))"

... produciendo una salida amigable de YAML como esta:

{'\n': 202,
 ' ': 2153,
 '!': 4,
 '"': 62,
 '#': 12,
 '%': 9,
 "'": 10,
 '(': 84,
 ')': 84,
 '*': 1,
 ',': 39,
 '-': 5,
 '.': 121,
 '/': 12,
 '0': 5,
 '1': 7,
 '2': 1,
 '3': 1,
 ':': 65,
 ';': 3,
 '<': 1,
 '=': 41,
 '>': 12,
 '@': 6,
 'A': 3,
 'B': 2,
 'C': 1,
 'D': 3,
 'E': 25}

Es interesante ver cómo la mayoría de las veces Python puede vencer fácilmente incluso a bash en términos de claridad de código.

revs Giampaolo Rodolà
fuente
11

Similar al awkmétodo de Guru :

perl -e 'while (<>) {$c{$&}++ while /./g} print "$c{$_} $_\n" for keys %c'
Gravedad
fuente
10

Después de usar UNIX durante un par de años, se vuelve muy competente al vincular varias operaciones pequeñas para llevar a cabo diversas tareas de filtrado y conteo. Cada uno tiene su propio estilo: algunos como awky sed, otros como cuty tr. Así es como lo haría:

Para procesar un nombre de archivo en particular:

 od -a FILENAME_HERE | cut -b 9- | tr " " \\n | egrep -v "^$" | sort | uniq -c

o como filtro:

 od -a | cut -b 9- | tr " " \\n | egrep -v "^$" | sort | uniq -c

Funciona así:

  1. od -a separa el archivo en caracteres ASCII.
  2. cut -b 9-elimina el prefijo odpone.
  3. tr " " \\n convierte los espacios entre caracteres en líneas nuevas para que haya un carácter por línea.
  4. egrep -v "^$" elimina todas las líneas en blanco adicionales que esto crea.
  5. sort reúne instancias de cada personaje juntos.
  6. uniq -c cuenta el número de repeticiones de cada línea.

Lo alimenté "¡Hola, mundo!" seguido de una nueva línea y obtuve esto:

  1 ,
  1 !
  1 d
  1 e
  1 H
  3 l
  1 nl
  2 o
  1 r
  1 sp
  1 w
David Schwartz
fuente
9

La sedparte se basa en la respuesta de @ Guru , aquí hay otro enfoque que utiliza uniq, similar a la solución de David Schwartz.

$ cat foo
aix
linux
bsd
foo
$ sed 's/\(.\)/\1\n/g' foo | sort | uniq -c
4 
1 a
1 b
1 d
1 f
2 i
1 l
1 n
2 o
1 s
1 u
2 x
Claudio
fuente
1
Use en [[:alpha:]]lugar de .en sedpara solo coincidir caracteres y no líneas nuevas.
Claudio
1
[[:alpha:]]fallará si también está tratando de combinar cosas como -, que se mencionó en la pregunta
Izkata
Correcto. Puede ser que sea más agradable para agregar una segunda expresión de sed para filtrar primero todo lo demás y luego coincidir explícitamente en los caracteres deseados: sed -e 's/[^ATCGN-]//g' -e 's/\([ATCGN-]\)/\1\n/g' foo | sort | uniq -c. Sin embargo, no sé cómo deshacerme de las nuevas líneas allí: \
Claudio
7

Puedes combinar grepy wchacer esto:

grep -o 'character' file.txt | wc -w

grepbusca en el (los) archivo (s) dado (s) el texto especificado, y la -oopción le dice que solo imprima las coincidencias reales (es decir, los caracteres que estaba buscando), en lugar del valor predeterminado que es imprimir cada línea en la que estaba el texto de búsqueda encontrado en.

wcimprime los recuentos de bytes, palabras y líneas para cada archivo, o en este caso, la salida del grepcomando. La -wopción le dice que cuente palabras, y cada palabra es una ocurrencia de su carácter de búsqueda. Por supuesto, la -lopción (que cuenta líneas) también funcionaría, ya que grepimprime cada aparición de su carácter de búsqueda en una línea separada.

Para hacer esto para varios caracteres a la vez, coloque los caracteres en una matriz y repítelo:

chars=(A T C G N -)
for c in "${chars[@]}"; do echo -n $c ' ' && grep -o $c file.txt | wc -w; done

Ejemplo: para un archivo que contiene la cadena TGC-GTCCNATGCGNNTCACANN-, la salida sería:

A  3
T  4
C  6
G  4
N  5
-  2

Para más información, vea man grepy man wc.


La desventaja de este enfoque, como señala el usuario Journeyman Geek a continuación en un comentario, es que grepdebe ejecutarse una vez para cada personaje. Dependiendo de qué tan grandes sean sus archivos, esto puede generar un impacto notable en el rendimiento. Por otro lado, cuando se hace de esta manera, es un poco más fácil ver rápidamente qué caracteres se están buscando y agregarlos / eliminarlos, ya que están en una línea separada del resto del código.

Indrek
fuente
3
tendrían que repetirlo por cada personaje que quieran ... Añadiría. Podría jurar que hay una solución más elegante, pero necesita más empuje; p
Journeyman Geek
@JourneymanGeek Buen punto. Un enfoque que me viene a la mente es poner a los personajes en una matriz y recorrerlos. He actualizado mi publicación.
Indrek
OMI demasiado complejo. Simplemente use grep -ea -et y así sucesivamente. Si lo coloca en una matriz y lo recorre, ¿no tendría que ejecutar el ciclo grep una vez por carácter?
Journeyman Geek
@JourneymanGeek Probablemente tengas razón. uniq -cTambién parece una mejor manera de obtener una salida bien formateada. No soy * nix guru, lo anterior es justo lo que logré reunir a partir de mi conocimiento limitado y algunas páginas de manual :)
Indrek
Yo también; p, y una de mis tareas el último trimestre implicó clasificar alrededor de 5000 entradas de la libreta de direcciones, y uniq lo hizo MUCHO más fácil.
Journeyman Geek
7

Usando las líneas de secuencia de 22hgp10a.txt, la diferencia de tiempo entre grep y awk en mi sistema hace que usar awk sea el camino a seguir ...

[Editar]: Después de haber visto la solución compilada de Dave, olvídate de awk también, ya que la completó en ~ 0.1 segundos en este archivo para contar con mayúsculas y minúsculas.

# A nice large sample file.
wget http://gutenberg.readingroo.ms/etext02/22hgp10a.txt

# Omit the regular text up to the start `>chr22` indicator.
sed -ie '1,/^>chr22/d' 22hgp10a.txt

sudo test # Just get sudo setup to not ask for password...

# ghostdog74 answered a question <linked below> about character frequency which
# gave me all case sensitive [ACGNTacgnt] counts in ~10 seconds.
sudo chrt -f 99 /usr/bin/time -f "%E elapsed, %c context switches" \
awk -vFS="" '{for(i=1;i<=NF;i++)w[$i]++}END{for(i in w) print i,w[i]}' 22hgp10a.txt

# The grep version given by Journeyman Geek took a whopping 3:41.47 minutes
# and yielded the case sensitive [ACGNT] counts.
sudo chrt -f 99 /usr/bin/time -f "%E elapsed, %c context switches" \
grep -o foo.text -e A -e T -e C -e G -e N -e -|sort|uniq -c

La versión insensible a mayúsculas y minúsculas de ghostdog se completó en ~ 14 segundos.

El sed se explica en la respuesta aceptada a esta pregunta .
La evaluación comparativa es como en la respuesta aceptada a esta pregunta .
La respuesta aceptada por ghostdog74 fue a esta pregunta .

Thell
fuente
1
Puedes s/cache[letters[x]]/cache[letters[x]]+cache[toupper(letters[x])]minar para que no se distinga entre mayúsculas y minúsculas sin afectar su velocidad.
Dave
6

Creo que cualquier implementación decente evita la clasificación. Pero debido a que también es una mala idea leer todo 4 veces, creo que de alguna manera se podría generar una secuencia que pasa por 4 filtros, uno para cada personaje, que se filtra y donde las longitudes de la secuencia también se calculan de alguna manera.

time cat /dev/random | tr -d -C 'AGCTN\-' | head -c16M >dna.txt
real    0m5.797s
user    0m6.816s
sys     0m1.371s

$ time tr -d -C 'AGCTN\-' <dna.txt | tee >(wc -c >tmp0.txt) | tr -d 'A' | 
tee >(wc -c >tmp1.txt) | tr -d 'G' | tee >(wc -c >tmp2.txt) | tr -d 'C' | 
tee >(wc -c >tmp3.txt) | tr -d 'T' | tee >(wc -c >tmp4.txt) | tr -d 'N' | 
tee >(wc -c >tmp5.txt) | tr -d '\-' | wc -c >tmp6.txt && cat tmp[0-6].txt

real    0m0.742s
user    0m0.883s
sys     0m0.866s

16777216
13983005
11184107
8387205
5591177
2795114
0

Las sumas acumulativas están entonces en tmp [0-6] .txt ... por lo que el trabajo aún está en progreso

Hay solo 13 tuberías en este enfoque, que se convierte en menos de 1 Mb de memoria.
Por supuesto, mi solución favorita es:

time cat >f.c && gcc -O6 f.c && ./a.out
# then type your favourite c-program
real    0m42.130s
Aki Suihkonen
fuente
Este es un muy buen uso de tr.
adavid
4

No sabía uniqni sobre grep -o, pero dado que mis comentarios sobre @JourneymanGeek y @ crazy2be tenían tanto apoyo, tal vez debería convertirlo en una respuesta propia:

Si sabe que solo hay caracteres "buenos" (aquellos que desea contar) en su archivo, puede buscar

grep . -o YourFile | sort | uniq -c

Si solo se deben contar algunos caracteres y otros no (es decir, separadores)

grep '[ACTGN-]' YourFile | sort | uniq -c

El primero usa el comodín de expresión regular ., que coincide con cualquier carácter individual. El segundo usa un 'conjunto de caracteres aceptados', sin un orden específico, excepto que -debe ser el último ( A-Cse interpreta como 'cualquier carácter entre Ay C). En ese caso, se requieren comillas para que su shell no intente expandirlo para verificar los archivos de un solo carácter si los hay (y producir un error de "no coincidencia" si no hay ninguno).

Tenga en cuenta que "sort" también tiene un -uindicador de nique, por lo que solo informa cosas una vez, pero no tiene un indicador complementario para contar duplicados, por lo que uniqes obligatorio.

sylvainulg
fuente
-no tiene que ser el último si se escapa con una barra invertida: '[A\-CTGN]'debería funcionar bien.
Indrek
2

Una tonta:

tr -cd ATCGN- | iconv -f ascii -t ucs2 | tr '\0' '\n' | sort | uniq -c
  • treliminar ( -d) todos los caracteres excepto ( -c) ATCGN-
  • iconv para convertir a ucs2 (UTF16 limitado a 2 bytes) para agregar un byte 0 después de cada byte,
  • otro trpara traducir esos caracteres NUL a NL. Ahora cada personaje está en su propia línea.
  • sort | uniq -cpara contar cada línea uniq

Esa es una alternativa a la -oopción grep no estándar (GNU) .

sch
fuente
¿Podría dar una breve explicación de los comandos y la lógica aquí?
Andrew Lambert
2
time $( { tr -cd ACGTD- < dna.txt | dd | tr -d A | dd | tr -d C | dd | tr -d G |
dd | tr -d T | dd | tr -d D | dd | tr -d - | dd >/dev/null; } 2>tmp ) &&
grep byte < tmp | sort -r -g | awk '{ if ((s-$0)>=0) { print s-$0} s=$0 }'

El formato de salida no es el mejor ...

real    0m0.176s
user    0m0.200s
sys     0m0.160s
2069046
2070218
2061086
2057418
2070062
2052266

Teoría de operación:

  • $ ({command | command} 2> tmp) redirige el stderr de la secuencia a un archivo temporal.
  • dd envía stdin a stdout y genera el número de bytes pasados ​​a stderr
  • tr -d filtra un caracter a la vez
  • grep and sort filtra la salida de dd a orden descendente
  • awk calcula la diferencia
  • sort se usa solo en la etapa de postprocesamiento para manejar la incertidumbre del orden de salida de las instancias de dd

La velocidad parece ser 60 MBps +

revs Aki Suihkonen
fuente
Mejoras: ¿deshacerse de tmp? usa 'pegar' para imprimir la carta involucrada?
Aki Suihkonen
1

Archivo de muestra:

$ cat file
aix
unix
linux

Mando:

$ sed 's/./&\n/g' file | awk '!/^$/{a[$0]++}END{for (i in a)print i,a[i];}'
u 2
i 3
x 3
l 1
n 2
a 1
Guru
fuente
-1 por falta de claridad y por publicar una frase sin explicación. AFAIK, esto podría ser una bomba tenedor
PPC
1

Combinando algunos otros

chars='abcdefghijklmnopqrstuvwxyz-'
grep -o -i "[$chars]" foo|sort | uniq -c

Agregue | sort -nrpara ver los resultados en orden de frecuencia.

Keith Wolters
fuente
1

Respuesta corta:

Si las circunstancias lo permiten, compare los tamaños de archivo de los conjuntos de caracteres bajos con uno sin caracteres para obtener un desplazamiento y solo cuente los bytes.

Ah, pero los detalles enredados:

Esos son todos los personajes Ascii. Un byte por. Los archivos, por supuesto, tienen metadatos adicionales antepuestos para una variedad de cosas utilizadas por el sistema operativo y la aplicación que lo creó. En la mayoría de los casos, esperaría que estos ocupen la misma cantidad de espacio independientemente de los metadatos, pero trataría de mantener circunstancias idénticas cuando pruebe por primera vez el enfoque y luego verifique que tenga un desplazamiento constante antes de no preocuparse por ello. El otro problema es que los saltos de línea generalmente involucran dos caracteres de espacio en blanco ASCII y cualquier pestaña o espacio sería uno cada uno. Si puede estar seguro de que estos estarán presentes y no hay forma de saber cuántos de antemano, dejaría de leer ahora.

Puede parecer una gran cantidad de restricciones, pero si puede establecerlas fácilmente, esto me parece el enfoque más fácil / de mejor rendimiento si tiene un montón de estos para mirar (lo que parece probable si eso es ADN). Comprobar la longitud de una tonelada de archivos y restar una constante sería mucho más rápido que ejecutar grep (o similar) en cada uno.

Si:

  • Estas son cadenas simples e ininterrumpidas en archivos de texto puro
  • Están en tipos de archivo idénticos creados por el mismo editor de texto sin formato de vainilla como Scite (pegar está bien siempre que verifique espacios / devoluciones) o algún programa básico que alguien escribió

Y dos cosas que tal vez no importen, pero las probaría primero

  • Los nombres de los archivos son de igual longitud.
  • Los archivos están en el mismo directorio.

Intente encontrar el desplazamiento haciendo lo siguiente:

Compare un archivo vacío con uno con algunos caracteres fácilmente contables por humanos con uno con algunos caracteres más. Si restar el archivo vacío de los otros dos archivos le da recuentos de bytes que coinciden con el recuento de caracteres, ya está. Verifique las longitudes de los archivos y reste esa cantidad vacía. Si desea intentar descubrir archivos de varias líneas, la mayoría de los editores adjuntan dos caracteres especiales de un byte para los saltos de línea, ya que Microsoft tiende a ignorar uno, pero al menos tendría que buscar caracteres de espacio en blanco, en cuyo caso bien podrías hacerlo todo con grep.

revs Erik Reppen
fuente
1

Manera Haskell :

import Data.Ord
import Data.List
import Control.Arrow

main :: IO ()
main = interact $
  show . sortBy (comparing fst) . map (length &&& head) . group . sort

funciona así:

112123123412345
=> sort
111112222333445
=> group
11111 2222 333 44 5
=> map (length &&& head)
(5 '1') (4 '2') (3 '3') (2 '4') (1,'5')
=> sortBy (comparing fst)
(1 '5') (2 '4') (3 '3') (4 '2') (5 '1')
=> one can add some pretty-printing here
...

compilando y usando:

$ ghc -O2 q.hs
[1 of 1] Compiling Main             ( q.hs, q.o )
Linking q ...
$ echo 112123123412345 | ./q
[(1,'\n'),(1,'5'),(2,'4'),(3,'3'),(4,'2'),(5,'1')]%       
$ cat path/to/file | ./q
...

No es bueno para grandes archivos tal vez.

ht.
fuente
1

Hack perl rápido:

perl -nle 'while(/[ATCGN]/g){$a{$&}+=1};END{for(keys(%a)){print "$_:$a{$_}"}}'
  • -n: Itera sobre las líneas de entrada pero no imprime nada por ellas
  • -l: Elimina o agrega saltos de línea automáticamente
  • while: iterar sobre todas las apariciones de los símbolos solicitados en la línea actual
  • END: Al final, imprimir resultados
  • %a: Hash donde se almacenan los valores

Los caracteres que no aparecen en absoluto no se incluirán en el resultado.

MvG
fuente