Comando que imprimirá o valor apenas uma vez, embora apareça várias vezes

8

Eu tenho um grande arquivo txt no qual os valores estão se repetindo várias vezes. Existe algum comando que eu possa usar que percorrerá o arquivo e se um valor aparecer uma vez, não o repita novamente?

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

Portanto, deve ser algo como isto:

S04   
HOH  
CL   
BME 

O fato é que tenho um grande número de valores diferentes, então não posso fazê-lo manualmente, como aqui.

djordje
fonte

Respostas:

11

Você pode usar o comando sortcom a opção --unique:

sort -u input-file

Se você deseja gravar o resultado em FILE em vez da saída padrão, use a opção --output=FILE:

sort -u input-file -o output-file

O comando uniqtambém pode ser aplicado. Nesse caso, as linhas idênticas devem ser consequenciais; portanto, a entrada deve ser classificada preliminarmente - graças a @RonJohn para esta observação:

sort input-file | uniq > output-file

Gosto do sortcomando para casos semelhantes, devido à sua simplicidade, mas se você trabalha com matrizes grandes, a awkabordagem da resposta de John1024 pode ser mais poderosa. Aqui está uma comparação de tempo entre as abordagens mencionadas, aplicada em um arquivo (com base no exemplo acima) com quase 5 milhões de linhas:

$ 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

Outra diferença significativa é a mencionada por @Ruslan :

sort -usomente imprimirá o resultado quando a entrada terminar, enquanto esse awkcomando imprimirá cada nova linha de resultado rapidamente (isso pode ser mais importante para a entrada canalizada do que para o arquivo).

Aqui está uma ilustração:

insira a descrição da imagem aqui

No exemplo acima, o loop (mostrado abaixo) gera 500 combinações aleatórias, cada uma com um comprimento de três caracteres, das letras AD. Essas combinações são canalizadas para awkou sort.

for i in {1..500}; do cat /dev/urandom | tr -dc A-D | head -c 3; echo; done
pa4080
fonte
1
É um comando muito simples! Muito Obrigado! Muito bem sucedida.
Djordje
2
Oh, nos dias em que um utilitário fazia uma coisa e fazia bem !! sort input-file | uniq!!!!
RonJohn
15

Se você deseja manter as linhas de saída na mesma ordem que as linhas de entrada, use:

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

Como funciona:

Isso usa uma matriz associativa apara contar o número de vezes que cada linha foi vista anteriormente. Se não tiver sido visto anteriormente, a linha será impressa.

John1024
fonte
2
É muito complicado awk, mas sort -ué o caminho mais fácil.
Pierre François
4
@ PierreFrançois, mas sort -utambém é o caminho mais lento :) Atualizei minha resposta com uma comparação de tempo entre as duas abordagens.
pa4080
4
Além disso, sort -uapenas imprimirá o resultado após o término da entrada, enquanto este awkcomando imprimirá cada nova linha de resultado rapidamente (isso pode ser mais importante para a entrada canalizada do que para o arquivo).
Ruslan
Obrigado por esta nota, @Ruslan! Eu tentei ilustrá-lo na minha resposta.
pa4080
Devo confessar que a awksolução é muito boa, embora não seja tão fácil de ler quanto sort.
Pierre François
1

Você pode usar o GNU datamash aqui também da seguinte forma e manterá a ordem das linhas.

datamash rmdup 1 < infile
αғsнιη
fonte
1
De acordo com a time comparação, esta é a solução mais rápida, fornecida aqui.
pa4080