Comando que solo imprimirá valor una vez, aunque aparezca muchas veces

8

Tengo un gran archivo txt en el que los valores se repiten muchas veces. ¿Hay algún comando que pueda usar que vaya a través del archivo y si aparece un valor una vez, no lo repita nuevamente?

SO4
HOH
CL
BME
HOH
SO4
HOH
CL
BME
HOH
SO4
HOH
SO4
HOH
CL
BME
HOH
SO4
HOH
CL
BME
HOH
CL

Entonces debería verse más o menos así:

S04   
HOH  
CL   
BME 

La cosa es que tengo una gran cantidad de valores diferentes, así que no puedo hacerlo manualmente como aquí.

djordje
fuente

Respuestas:

11

Puede usar el comando sortcon la opción --unique:

sort -u input-file

Si desea escribir el resultado en ARCHIVO en lugar de la salida estándar, use la opción --output=FILE:

sort -u input-file -o output-file

El comando uniqtambién podría aplicarse. En este caso, las líneas idénticas deben ser consecuentes, por lo que la entrada debe clasificarse de forma preliminar, gracias a @RonJohn por esta nota:

sort input-file | uniq > output-file

Me gusta el sortcomando para casos similares, debido a su simplicidad, pero si trabaja con matrices grandes, el awkenfoque de la respuesta de John1024 podría ser más poderoso. Aquí hay una comparación de tiempo entre los enfoques mencionados, aplicada en un archivo (basado en el ejemplo anterior) con casi 5 millones de líneas:

$ cat input-file | wc -l
20000000

$ TIMEFORMAT=%R
$ time sort -u input-file | wc -l
64
7.495

$ time sort input-file | uniq | wc -l
64
7.703

$ time awk '!a[$0]++' input-file | wc -l      # from John1024's answer
64
1.271

$ time datamash rmdup 1 < input-file | wc -l  # from αғsнιη's answer
64
0.770

Otra diferencia significativa es la mencionada por @Ruslan :

sort -usolo imprimirá el resultado una vez que la entrada haya finalizado, mientras que este awkcomando imprimirá cada nueva línea de resultado sobre la marcha (esto puede ser más importante para la entrada canalizada que el archivo).

Aquí hay una ilustración:

ingrese la descripción de la imagen aquí

En el ejemplo anterior, el bucle (que se muestra a continuación) genera 500 combinaciones aleatorias, cada una con una longitud de tres caracteres, de las letras AD. Estas combinaciones se canalizan al awko sort.

for i in {1..500}; do cat /dev/urandom | tr -dc A-D | head -c 3; echo; done
pa4080
fuente
1
Es un comando muy simple! ¡Muchas gracias! Todo lo mejor.
djordje
2
¡Oh, para los días en que una empresa de servicios públicos hizo una cosa y lo hizo bien! sort input-file | uniq!!!!
RonJohn
15

Si desea mantener las líneas de salida en el mismo orden que las líneas de entrada, use:

$ awk '!a[$0]++' file
SO4
HOH
CL
BME

Cómo funciona:

Esto usa una matriz asociativa apara contar la cantidad de veces que cada línea se ha visto previamente. Si no se ha visto previamente, la línea se imprime.

John1024
fuente
2
Es muy complicado awk, pero sort -ues la forma fácil.
Pierre François
44
@ PierreFrançois, pero sort -utambién es la forma más lenta :) He actualizado mi respuesta con una comparación de tiempo entre los dos enfoques.
pa4080
44
Además, sort -usolo imprimirá el resultado una vez que la entrada haya finalizado, mientras que este awkcomando imprimirá cada nueva línea de resultado sobre la marcha (esto puede ser más importante para la entrada canalizada que el archivo).
Ruslan
Gracias por esta nota, @Ruslan! He tratado de ilustrarlo en mi respuesta.
pa4080
Debo confesar que la awksolución es muy buena, aunque no tan fácil de leer como sort.
Pierre François
1

Puede usar GNU datamash aquí también de la siguiente manera, y mantendrá el orden de las líneas.

datamash rmdup 1 < infile
αғsнιη
fuente
1
Según la time comparación, esta es la solución más rápida, proporcionada aquí.
pa4080