Como obter apenas os resultados exclusivos sem precisar classificar os dados?

40
$ cat data.txt 
aaaaaa
aaaaaa
cccccc
aaaaaa
aaaaaa
bbbbbb
$ cat data.txt | uniq
aaaaaa
cccccc
aaaaaa
bbbbbb
$ cat data.txt | sort | uniq
aaaaaa
bbbbbb
cccccc
$

O resultado que eu preciso é exibir todas as linhas do arquivo original, removendo todas as duplicatas (não apenas as consecutivas), mantendo a ordem original das instruções no arquivo .

Aqui, neste exemplo, o resultado que eu realmente estava procurando era

aaaaaa
cccccc
bbbbbb

Como posso executar esta uniqoperação generalizada em geral?

lazer
fonte

Respostas:

54
perl -ne 'print unless $seen{$_}++' data.txt

Ou, se você deve ter um uso inútil decat :

cat data.txt | perl -ne 'print unless $seen{$_}++'

Aqui está uma awktradução, para sistemas que não possuem Perl:

awk '!seen[$0]++' data.txt
cat data.txt | awk '!seen[$0]++'
cjm
fonte
3
Um script awk ligeiramente mais curto é{ if (!seen[$0]++) print }
CAMH
11
@fred, a menos que seu arquivo seja realmente grande, qualquer uma das versões leva mais tempo para digitar do que para ser executada.
CJM
8
A versão awk pode ser feita ainda mais curto, deixando de fora os if, print, parênteses e chaves:awk '!seen[$0]++'
Gordon Davisson
2
@ Legate, é o nome de uma matriz na qual estamos gravando todas as linhas que vimos. Você pode alterá-lo '!LarryWall[$0]++'para todos os cuidados com awk, mas "visto" ajuda as pessoas a entender melhor o programa.
Cjm
11
@ Sadi, isso realmente deveria ter sido feito como uma pergunta, não como um comentário. Mas algumas das linhas desse arquivo terminam em um espaço e outras não. Esses comandos consideram a linha inteira significativa, incluindo o espaço em branco no final.
Cjm
13

john tem uma ferramenta chamada unique:

usr@srv % cat data.txt | unique out
usr@srv % cat out
aaaaaa
cccccc
bbbbbb

Conseguir o mesmo sem ferramentas adicionais em uma única linha de comando é um pouco mais complexo:

usr@srv % cat data.txt | nl | sort -k 2 | uniq -f 1 | sort -n | sed 's/\s*[0-9]\+\s\+//'
aaaaaa
cccccc
bbbbbb

nlimprime números de linhas na frente das linhas; portanto, se nós sort/ uniqatrás deles, podemos restaurar a ordem original das linhas. sedapenas exclui os números de linha depois;)

binfalse
fonte
existe alguma combinação de comandos linux comuns que possam fazer o mesmo?
Lazer
7
O que você perdeu "sem ter que classificar os dados"?
Totor 29/07
@ Motor - veja a resposta do menkus a um comentário semelhante. @ binfalse - sua segunda solução não funciona (talvez funcione com essa amostra trivial, mas não funciona com algumas informações da vida real). Corrija que, por exemplo, esta deve sempre trabalho:nl -ba -nrz data.txt | sort -k2 -u | sort | cut -f2
don_crissti
6

Eu prefiro usar isso:

cat -n data.txt | sort --key=2.1 -b -u | sort -n | cut -c8-

cat -n adiciona números de linha,

sort --key=2.1 -b -u classifica no segundo campo (após os números de linha adicionados), ignorando espaços em branco à esquerda, mantendo linhas exclusivas

sort -n classifica em ordem numérica estrita

cut -c8- mantenha todos os caracteres da coluna 8 à EOL (ou seja, omita os números de linha que incluímos)

menkus
fonte
5
> Como obter apenas resultados exclusivos sem precisar classificar os dados? > sem ter a dados de classificação
Jan Wikholm
7
'sem ter que classificar os dados' aparece apenas no título. A necessidade real é: "exibir todas as linhas do arquivo original removendo todas as duplicatas (não apenas as consecutivas), mantendo a ordem original das instruções no arquivo".
menkus
11
@menkus, a chave é "mantendo a ordem original das instruções no arquivo". Esta resposta não consegue isso.
Andrew Ferrier
2

O Perl possui um módulo que você pode usar que inclui uma função chamada uniq. Portanto, se você tiver seus dados carregados em uma matriz no Perl, basta chamar a função como esta para torná-la única, mas ainda assim manter a ordem original.

use List::MoreUtils qw(uniq)    
@output = uniq(@output);

Você pode ler mais sobre este módulo aqui: List :: MoreUtils

slm
fonte
Isso pode lidar com arquivos enormes, por exemplo, 500 GB?
Garoto