Commande qui n'imprimera la valeur qu'une seule fois bien qu'elle apparaisse plusieurs fois

8

J'ai un gros fichier txt dans lequel les valeurs se répètent plusieurs fois. Y a-t-il une commande que je peux utiliser qui passera par le fichier et si une valeur apparaît une fois, ne la répétez pas?

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

Il devrait donc ressembler à ceci:

S04   
HOH  
CL   
BME 

Le fait est que j'ai un grand nombre de valeurs différentes, donc je ne peux pas le faire manuellement comme ici.

djordje
la source

Réponses:

11

Vous pouvez utiliser la commande sortavec l'option --unique:

sort -u input-file

Si vous souhaitez écrire le résultat dans FILE au lieu de la sortie standard, utilisez l'option --output=FILE:

sort -u input-file -o output-file

La commande uniqpourrait également être appliquée. Dans ce cas, les lignes identiques doivent être consécutives, donc l'entrée doit être triée préalablement - merci à @RonJohn pour cette note:

sort input-file | uniq > output-file

J'aime la sortcommande pour des cas similaires, en raison de sa simplicité, mais si vous travaillez avec de grands tableaux, l' awkapproche de la réponse de John1024 pourrait être plus puissante. Voici une comparaison temporelle entre les approches mentionnées, appliquées sur un fichier (basé sur l'exemple ci-dessus) de près de 5 millions de lignes:

$ 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

Une autre différence significative est celle mentionnée par @Ruslan :

sort -uimprimera uniquement le résultat une fois l'entrée terminée, tandis que cette awkcommande imprimera chaque nouvelle ligne de résultat à la volée (cela peut être plus important pour l'entrée canalisée que pour le fichier).

En voici une illustration:

entrez la description de l'image ici

Dans l'exemple ci-dessus, la boucle (illustrée ci-dessous) génère 500 combinaisons aléatoires, chacune d'une longueur de trois caractères, des lettres AD. Ces combinaisons sont dirigées vers awkou sort.

for i in {1..500}; do cat /dev/urandom | tr -dc A-D | head -c 3; echo; done
pa4080
la source
1
C'est une commande très simple! Merci beaucoup! Bonne chance.
djordje
2
Oh, pour les jours où un utilitaire a fait une chose et l'a bien fait !! sort input-file | uniq!!!!
RonJohn
15

Si vous souhaitez conserver les lignes de sortie dans le même ordre que les lignes d'entrée, utilisez:

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

Comment ça fonctionne:

Cela utilise un tableau associatif apour compter le nombre de fois où chaque ligne a été vue précédemment. S'il n'a pas été vu auparavant, la ligne est imprimée.

John1024
la source
2
C'est très délicat avec awk, mais sort -uc'est le moyen facile.
Pierre François
4
@ PierreFrançois, mais c'est sort -uaussi le chemin le plus lent :) J'ai mis à jour ma réponse avec une comparaison temporelle entre les deux approches.
pa4080
4
En outre, sort -un'imprimera le résultat qu'une fois l'entrée terminée, tandis que cette awkcommande imprimera chaque nouvelle ligne de résultat à la volée (cela peut être plus important pour l'entrée canalisée que pour le fichier).
Ruslan
Merci pour cette note, @Ruslan! J'ai essayé de l'illustrer dans ma réponse.
pa4080
Je dois avouer que la awksolution est très bonne, mais pas aussi facile à lire que sort.
Pierre François
1

Vous pouvez également utiliser GNU datamash ici comme suit et conserver l'ordre des lignes.

datamash rmdup 1 < infile
αғsнιη
la source
1
Selon la time comparaison, c'est la solution la plus rapide, fournie ici.
pa4080