Qual é a diferença entre “sort -u” e “sort | uniq ”?

102

Em todos os lugares que vejo alguém precisando obter uma lista única e classificada, eles sempre encaminham para sort | uniq . Eu nunca vi nenhum exemplo em que alguém usa sort -u . Por que não? Qual é a diferença e por que é melhor usar o uniq do que o sinalizador exclusivo para classificar?

    
por Benubird 16.05.2013 / 13:22

6 respostas

102

sort | uniq existia antes de sort -u e é compatível com uma ampla gama de sistemas, embora quase todos os sistemas modernos suportem -u - é POSIX. É principalmente um retrocesso para os dias em que sort -u não existia (e as pessoas não tendem a mudar seus métodos se a maneira como elas sabem continuar a funcionar, basta olhar para ifconfig vs. ip adoption).

Os dois provavelmente foram mesclados porque a remoção de duplicatas em um arquivo requer classificação (pelo menos, no caso padrão) e é um caso de uso extremamente comum de classificação. Ele também é mais rápido internamente como resultado de poder executar as duas operações ao mesmo tempo (e devido ao fato de não exigir IPC entre uniq e sort ). Especialmente se o arquivo for grande, sort -u provavelmente usará menos arquivos intermediários para classificar os dados.

No meu sistema, eu sempre obtenho resultados como este:

$ dd if=/dev/urandom of=/dev/shm/file bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 8.95208 s, 11.7 MB/s
$ time sort -u /dev/shm/file >/dev/null

real        0m0.500s
user        0m0.767s
sys         0m0.167s
$ time sort /dev/shm/file | uniq >/dev/null

real        0m0.772s
user        0m1.137s
sys         0m0.273s

Ele também não mascara o código de retorno de sort , o que pode ser importante (nos shells modernos existem maneiras de obter isso, por exemplo, bash ' $PIPESTATUS array, mas isso não foi sempre verdade).

    
por 16.05.2013 / 13:28
41

Uma diferença é que uniq tem várias opções adicionais úteis, como ignorar campos para comparação e contar o número de repetições de um valor. O sort flag de -u implementa apenas a funcionalidade do comando uniq não adulterado.

    
por 16.05.2013 / 15:35
37

Com os sort s e uniq s compatíveis com POSIX (o% GNU uniq não está atualmente em conformidade a esse respeito), há uma diferença em que sort usa o algoritmo de intercalação do código de idioma para comparar cadeias (normalmente usará strcoll() para comparar cadeias) enquanto uniq verifica identidade de valor de byte (normalmente usa strcmp() ).

Isso é importante por pelo menos dois motivos.

  • Em algumas localidades, especialmente em sistemas GNU, existem diferentes caracteres que ordenam o mesmo. Por exemplo, no código de idioma en_US.UTF-8 em um sistema GNU, todos os caracteres ①②③④⑤⑥⑦⑧⑨⑩ ... e muitos outros ordenam o mesmo, porque sua ordem de classificação não está definida. Os algarismos arábicos 0123456789 classificam-se da mesma forma que os correspondentes Eastern Arabic Indic (٠١٢٣٤٥٦٧٨٩).

    Para sort -u , ① ordena o mesmo que ② e 0123 é o mesmo que ٠١٢٣ então sort -u reteria apenas um de cada, enquanto para uniq (não GNU uniq que usa strcoll() (exceto com -i )), ① é diferente de ② e 0123 diferente de ٠١٢٣, então uniq consideraria todos os 4 únicos.

  • strcoll só pode comparar strings de caracteres válidos (o comportamento é indefinido como por POSIX quando a entrada possui sequências de bytes que não formam caracteres válidos) enquanto strcmp() não se importa com caracteres, pois só faz byte-to comparação de bytes. Essa é outra razão pela qual sort -u pode não fornecer todas as linhas exclusivas se algumas delas não formarem texto válido. sort|uniq , embora ainda não especificado na entrada sem texto, na prática, é mais provável que você forneça linhas exclusivas por esse motivo.

Além dessas sutilezas, uma coisa que não foi notada até agora é que uniq compara a linha inteira lexicamente, enquanto sort ' -u compara com base na especificação de classificação dada na linha de comando.

$ printf '%s\n' 'a b' 'a c' | sort -uk 1,1
a b
$ printf '%s\n' 'a b' 'a c' | sort -k 1,1 | uniq
a b
a c

$ printf '%s\n' 0 -0 +0 00 '' | sort -n | uniq
0
-0
+0
00

$ printf '%s\n' 0 -0 +0 00 '' | sort -nu
0
    
por 16.05.2013 / 19:18
8

Eu prefiro usar sort | uniq , porque quando eu tento usar a opção -u (eliminar duplicatas) para remover duplicatas envolvendo sequências de maiúsculas e minúsculas, não é fácil entender o resultado.

Observação: antes de executar os exemplos abaixo, é necessário simular a sequência de intercalação padrão C fazendo o seguinte:

LC_ALL=C
export LC_ALL

Por exemplo, se eu quiser classificar um arquivo e remover duplicatas, enquanto, ao mesmo tempo, mantenho os diferentes casos de seqüências de caracteres distintos.

$ cat short      #file to sort
Pear
Pear
apple
pear
Apple

$ sort short     #normal sort (in normal C collating sequence)
Apple            #the lower case words are at the end
Pear
Pear
apple
pear

$ sort -f short  #correctly sorts ignoring the C collating order
Apple            #but duplicates are still there
apple
Pear
Pear
pear

$ sort -fu short #By adding the -u option to remove duplicates it is 
apple            #difficult to ascertain the logic that sort uses to remove
Pear             #duplicates(i.e., why did it remove pear instead of Pear?)

Esta confusão é resolvida por não usar a opção -u para remover duplicatas. Usar uniq é mais previsível. O abaixo primeiro classifica e ignora o caso e depois passa para uniq para remover as duplicatas.

$ sort -f short | uniq
Apple
apple
Pear
pear
    
por 06.06.2015 / 08:04
2

Outra diferença que descobri hoje é quando classifico com base em um delimitador em que sort -u aplica o sinalizador exclusivo somente na coluna com a qual você classifica.

$ cat input.csv
3,World,1
1,Hello,1
2,Hello,1

$ cat input.csv | sort -t',' -k2 -u
1,Hello,1
3,World,1

$ cat input.csv | sort -t',' -k2 | uniq
1,Hello,1
2,Hello,1
3,World,1
    
por 06.01.2017 / 09:52
-1
$ cat need2sort
pma2005
PMA2005
pma2005

$ LC_CTYPE="C" sort -fu need2sort
pma2005

$ LC_CTYPE="C" sort -f need2sort | uniq
PMA2005
pma2005
    
por 19.11.2014 / 04:22

Tags