¿Cómo es que uniq no es lo suficientemente único como para que también haya uniq --unique?

35

Aquí hay comandos en un archivo aleatorio de pastebin :

wget -qO - http://pastebin.com/0cSPs9LR | wc -l
350
wget -qO - http://pastebin.com/0cSPs9LR | sort -u | wc -l
287
wget -qO - http://pastebin.com/0cSPs9LR | sort | uniq | wc -l
287
wget -qO - http://pastebin.com/0cSPs9LR | sort | uniq -u | wc -l
258

Las páginas del manual no tienen claro qué -uestá haciendo la bandera. ¿Algún consejo?

enfascination
fuente
44
Intenta ordenar | uniq -d | wc -l y podrías notar la diferencia. :)
stoeff

Respuestas:

42

Version corta:

  • uniq, sin -u, hace que cada línea de la salida sea única.
  • uniq -usolo imprime cada línea única desde la entrada .

Versión ligeramente más larga:

uniqes para tratar con archivos que tienen líneas duplicadas, y solo cuando esas líneas aparecen sucesivamente en la entrada. Entonces, para sus propósitos, una línea única es aquella que no se duplica inmediatamente.

( uniqtiene una memoria a corto plazo muy limitada; nunca recordará si una línea apareció antes en la entrada, a menos que fuera la línea inmediatamente anterior; esta es la razón por la cual uniqse combina muy a menudo sort).

Cuando encuentra una serie de líneas duplicadas uniq, sin el -uargumento, imprime una copia de esa línea. (Hace que cada línea de la salida sea única ).

Con el -uargumento, imprime cero copias de esa línea: las ejecuciones de duplicados simplemente se omiten de la salida.

Ian Clelland
fuente
1
Realmente desearía que hubiera una opción para no requerir clasificación. Pero requeriría mantener todo el archivo en la memoria (o hacer un montón de contabilidad con los hashes y compensaciones si la fuente es un archivo normal)
Random832
3
@ Random832: y requeriría decidir cuál de los engaños mantener (primero, último, algo más, configurable), y esa decisión afectaría el algoritmo globalmente. Molestia.
Steve Jessop
1
@ Random832: si solo se trata de la cantidad de caracteres a escribir, puede usar en sort -ulugar de sort | uniq.
Oliver
@oliver De vez en cuando quería tener la capacidad de mantener la primera instancia de cualquier línea sin reorganizarlas, y escribí guiones para hacerlo.
Aleatorio832
1
@hvd: si su versión de uniqhace normalización y colación, sí. Pero incluso entonces es solo una consideración local: usted sabe dónde aparecerá la línea en la salida ordenada, y solo tiene que seleccionar cuál de varias líneas adyacentes desea mantener. Si la entrada no está ordenada, entonces la decisión afecta a toda la operación de unificación, por ejemplo, si va a mantener el último duplicado, no puede generar nada hasta que haya leído la última línea de la entrada ...
Steve Jessop
53

uniqcon -uomite cualquier línea que tenga duplicados. Así:

$ printf "%s\n" 1 1 2 3 | uniq
1
2
3
$ printf "%s\n" 1 1 2 3 | uniq -u
2
3

Por lo general, uniqimprime líneas como máximo una vez (suponiendo una entrada ordenada). Esta opción realmente imprime líneas que son verdaderamente únicas (que no han vuelto a aparecer).

muru
fuente
11
Es decir, uniqpodría llamarse distinct, ya que imprime todas las líneas distintas, mientras que uniq -uimprime todas las líneas únicas.
Steve Jessop
No es realmente único con GNU uniqen alguna configuración regional.
Cuonglm
Debo haber leído la respuesta aceptada varias veces, pero no se asimiló. Su ejemplo y párrafo después lo dejan muy claro (y volviendo y releyendo la respuesta aceptada, también lo entiendo) :)
Madivad
18

la especificación POSIX de uniq lo describió claramente:

-u
    Suppress the writing of lines that are repeated in the input.

-uopción hacer uniqno imprimir líneas repetidas.

La mayoría de las uniqimplementaciones usaban comparación de bytes, mientras que GNU uniqusaba el orden de clasificación para filtrar líneas duplicadas. Por lo tanto, puede producir un resultado incorrecto en algunas configuraciones en_US.UTF-8regionales , por ejemplo, en la configuración regional:

$ printf '%b\n' '\U2460' '\U2461' | uniq
①

y -uno te dio líneas:

$ printf '%b\n' '\U2460' '\U2461' | uniq -u
<blank>

Por lo tanto, debe establecer la configuración regional Cpara obtener la comparación de bytes:

$ printf '%b\n' '\U2460' '\U2461' | LC_ALL=C uniq
①
②
Cuonglm
fuente
3
Tenga en cuenta que lo que está mal aquí no es tanto uniq(aunque aparentemente la intención de POSIX era que debería hacer una comparación de bytes en lugar de una comparación strcoll () como en sort -u) como aquellas configuraciones regionales que erróneamente tienen ① ordenando lo mismo que ②. Al menos GNU uniqes consistente con sort -u.
Stéphane Chazelas
@ StéphaneChazelas: ¿en qué parte de la especificación se hace evidente?
mikeserv
Sobre lo uniqrequerido para hacer memcmp / strcmp en lugar de strcoll, eso no es muy evidente para mí, pero eso fue para Geoff . Sobre las configuraciones regionales de GNU que tienen ① ordenando lo mismo que ②, eso es claramente un error ya que no hay razón por la que deberían ordenar lo mismo. POSIX lo permite, pero se avecinan algunos cambios .
Stéphane Chazelas
8

normal:

echo "a b a b c c c" | tr ' ' '\n'
a
b
a
b
c
c
c

uniq: no hay dos líneas repetidas posteriores

echo "a b a b c c c" | tr ' ' '\n' | uniq
a
b
a
b
c

ordenado

echo "a b a b c c c" | tr ' ' '\n' | sort
a
a
b
b
c
c
c

sort -u: no hay dos líneas repetidas

echo "a b a b c c c" | tr ' ' '\n' | sort -u
a
b
c

sort / uniq: todos distintos

echo "a b a b c c c" | tr ' ' '\n' | sort | uniq
a
b
c

cuenta sucesos distintos

echo "a b a b c c c" | tr ' ' '\n' | sort | uniq -c
2 a
2 b
3 c

solo líneas que no se repiten (no se ordenan primero)

echo "a b a b c c c" | tr ' ' '\n' | uniq -u
a
b
a
b

solo líneas que no se repiten (después de ordenar)

echo "a b a b c c c Z" | tr ' ' '\n' | sort | uniq -u
Z

uniq -d: solo imprime líneas duplicadas, una para cada grupo

echo "a b a b c c c" | tr ' ' '\n' | uniq -d
c

.. contado

echo "a b a b c c c" | tr ' ' '\n' | uniq -dc
3 c
jmullee
fuente
buenos ejemplos claros :)
Madivad