Como o uniq não é único o suficiente para que também exista uniq --unique?

35

Aqui estão os comandos em um arquivo aleatório do 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

As páginas de manual não são claras sobre o que a -ubandeira está fazendo. Algum conselho?

enfascinação
fonte
4
Tente classificar | uniq -d | wc -l e você pode perceber a diferença. :)
stoeff

Respostas:

42

Versão curta:

  • uniq, sem -u, torna cada linha da saída única.
  • uniq -uapenas imprime todas as linhas exclusivas da entrada .

Versão ligeiramente mais longa:

uniqé para lidar com arquivos que tenham linhas duplicadas e somente quando essas linhas aparecerem sucessivamente na entrada. Portanto, para seus propósitos, uma linha exclusiva é aquela que não é duplicada imediatamente.

( uniqpossui uma memória de curto prazo muito limitada; nunca se lembrará se uma linha apareceu anteriormente na entrada, a menos que fosse a linha imediatamente anterior - é por isso que uniqmuitas vezes é emparelhada sort.)

Quando encontra uma sequência de linhas duplicadas uniq, sem o -uargumento, imprime uma cópia dessa linha. (Torna cada linha da saída única ).

Com o -uargumento, ele imprime zero cópias dessa linha - execuções de duplicatas são apenas omitidas na saída.

Ian Clelland
fonte
1
Eu realmente gostaria que houvesse uma opção para não exigir classificação. Mas isso exigiria manter o arquivo inteiro na memória (ou fazendo lotes de contabilidade com hashes e deslocamentos se a fonte é um arquivo normal)
Random832
3
@ Random832: e exigiria decidir qual dos enganos manter (primeiro, último, outra coisa, configurável), e essa decisão afetaria o algoritmo globalmente. Aborrecimento.
Steve Jessop
1
@ Random832: se é apenas o número de caracteres a digitar, você pode usar em sort -uvez de sort | uniq.
Oliver
@oliver Ocasionalmente, eu queria manter a primeira instância de qualquer linha sem reorganizá-las e escrever scripts para isso.
Random832
1
@ DVD: se a sua versão do uniqfaz normalização e agrupamento, sim. Mas, mesmo assim, é apenas uma consideração local - você sabe onde na linha classificada a linha aparecerá e só precisa selecionar qual das várias linhas adjacentes manter. Se a entrada não for classificada, a decisão afetará toda a operação de uniqificação, por exemplo, se você manterá a última duplicata, não poderá produzir nada até ler a última linha da entrada ...
Steve Jessop
53

uniqcom -usaltos quaisquer linhas que têm duplicados. Portanto:

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

Normalmente, uniqimprime linhas no máximo uma vez (assumindo entrada classificada). Esta opção realmente imprime linhas verdadeiramente únicas (não tendo aparecido novamente).

muru
fonte
11
Ou seja, uniqpoderia ser chamado distinct, pois imprime todas as linhas distintas, enquanto uniq -uimprime todas as linhas exclusivas.
Steve Jessop
Não é verdadeiramente único com o GNU uniqem algumas localidades.
cuonglm
Devo ter lido a resposta aceita várias vezes, mas ela não foi incluída. Seu exemplo e parágrafo depois deixam muito claro (e voltando e relendo a resposta aceita, também entendi) :)
Madivad
18

A especificação uniq POSIX descreveu claramente:

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

-uopção faça uniqpara não imprimir linhas repetidas.

A maioria das uniqimplementações usava comparação de bytes, enquanto o GNU uniqusava ordem de agrupamento para filtrar linhas duplicadas. Portanto, ele pode produzir resultados errados em algumas localidades, por exemplo, na en_US.UTF-8localidade:

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

e não -udeu linhas:

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

Portanto, você deve definir o código do idioma Cpara obter uma comparação de bytes:

$ printf '%b\n' '\U2460' '\U2461' | LC_ALL=C uniq
①
②
cuonglm
fonte
3
Observe que o que está errado aqui não é tanto uniq(embora aparentemente a intenção do POSIX seja que ele faça comparação de bytes em vez de comparação de strcoll () como em sort -u)) como os locais que erroneamente têm a mesma classificação de ②. Pelo menos o GNU uniqé consistente com sort -u.
Stéphane Chazelas
@ StéphaneChazelas - onde nas especificações isso é aparente?
mikeserv
Sobre uniqa necessidade de executar o memcmp / strcmp em oposição ao strcoll, isso não é muito aparente para mim, mas sim para Geoff . Sobre as localidades do GNU terem 'a mesma classificação que', isso é claramente um erro, pois não há razão para que eles devam classificar da mesma forma. Isso é permitido pelo POSIX, mas há algumas mudanças por vir .
Stéphane Chazelas
8

normal:

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

uniq: não há duas linhas de repetição subsequentes

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: não há duas linhas 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

conta ocorrências distintas

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

somente linhas que não são repetidas (não classificadas primeiro)

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

apenas linhas que não são repetidas (após a classificação)

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

uniq -d: imprime apenas linhas duplicadas, uma 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
fonte
agradáveis exemplos claros :)
Madivad