Comando que só imprime valor uma vez, embora apareça várias vezes

8

Eu tenho um grande arquivo txt no qual os valores estão repetindo muitas vezes. Existe algum comando que eu possa usar que irá passar por arquivo e se um valor aparece uma vez não repeti-lo novamente?

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

Por isso, deve ser algo como isto:

S04   
HOH  
CL   
BME 

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

    
por djordje 06.01.2018 / 09:26

3 respostas

11

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

sort -u input-file

Se você quiser 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 uniq também pode ser aplicado. Neste caso, as linhas idênticas devem ser consequentes, portanto a entrada deve ser classificada preliminarmente - graças a @RonJohn por esta nota:

sort input-file | uniq > output-file

Eu gosto do comando sort para casos semelhantes, devido à sua simplicidade, mas se você trabalha com grandes matrizes, a abordagem awk de John1024's answer poderia ser mais poderoso. 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 é que mencionado por @Ruslan :

sort -u will only print the result once the input has ended, while this awk command will do print each new result line on the fly (this may be more important for piped input than file).

Aqui está uma ilustração:

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

for i in {1..500}; do cat /dev/urandom | tr -dc A-D | head -c 3; echo; done
    
por pa4080 06.01.2018 / 09:35
15

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

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

Como funciona:

Isso usa o array associativo a para contar o número de vezes que cada linha foi vista anteriormente. Se não foi visto anteriormente, a linha é impressa.

    
por John1024 06.01.2018 / 09:36
1

Você pode usar GNU datamash aqui também da seguinte maneira e manterá a ordem da linha.

datamash rmdup 1 < infile
    
por αғsнιη 27.03.2018 / 20:05