275 votes

Meilleure façon de simuler "group by" à partir de bash

Supposons que vous avez un fichier qui contient des adresses IP, une adresse unique à chaque ligne:

10.0.10.1
10.0.10.1
10.0.10.3
10.0.10.2
10.0.10.1

Vous avez besoin d'un script shell qui compte pour chaque adresse IP, combien de fois qu'il apparaît dans le fichier. Pour l'entrée précédente vous avez besoin de la sortie suivante:

10.0.10.1 3
10.0.10.2 1
10.0.10.3 1

Une façon de le faire est:

cat ip_addresses |uniq |while read ip
do
    echo -n $ip" "
    grep -c $ip ip_addresses
done

Cependant il est très loin d'être efficace.

Comment voulez-vous résoudre ce problème de manière plus efficace à l'aide de bash?

(Une chose à ajouter: je sais qu'il peut être résolu à partir de perl ou awk, je suis intéressé par une meilleure solution dans bash, pas dans ces langues.)

INFOS SUPPLÉMENTAIRES:

Supposons que le fichier source est de 5 go et la machine qui exécute l'algorithme a 4 go. Si le tri n'est pas une solution efficace, ni de la lecture du fichier plusieurs fois.

J'ai aimé la table de hachage comme solution - n'importe qui peut apporter des améliorations à cette solution?

PLUS D'INFOS #2:

Certaines personnes ont demandé pourquoi je prendrais la peine de le faire en bash quand il est plus facile par exemple en perl. La raison en est que, sur la machine que j'avais à faire cette perl n'était pas disponible pour moi. C'était une coutume construit machine linux sans la plupart des outils que j'y suis habitué. Et je pense que c'était un problème intéressant.

Donc, s'il vous plaît, ne les blâme pas la question, juste l'ignorer si vous ne l'aimez pas. :-)

488voto

Joachim Sauer Points 133411
 sort ip_addresses | uniq -c
 

Cela imprimera d'abord le compte, mais à part ça, il devrait être exactement ce que vous voulez.

65voto

La méthode rapide et sale est la suivante:

cat ip_addresses | sort -n | uniq -c

Si vous devez utiliser les valeurs de bash, vous pouvez affecter la commande entière à une variable bash, puis parcourir les résultats.

PS

Si la commande de tri est omise, vous n'obtiendrez pas les résultats corrects car uniq ne regarde que les lignes successives identiques.

32voto

Anonymous Points 1

pour récapituler plusieurs champs, en fonction d'un groupe de champs existants, utilisez l'exemple ci-dessous: (remplacez les 1, 2, 3, 4 $ selon vos besoins)

 cat file

US|A|1000|2000
US|B|1000|2000
US|C|1000|2000
UK|1|1000|2000
UK|1|1000|2000
UK|1|1000|2000

awk 'BEGIN { FS=OFS=SUBSEP="|"}{arr[$1,$2]+=$3+$4 }END {for (i in arr) print i,arr[i]}' file

US|A|3000
US|B|3000
US|C|3000
UK|1|9000
 

25voto

Diomidis Spinellis Points 8417

La solution canonique est celui qui est mentionné par un autre répondant:

sort | uniq -c

Il est plus court et plus concis que ce qui peut être écrit en Perl ou awk.

Vous écrivez que vous ne souhaitez pas utiliser la sorte, parce que la taille est plus grande que la machine de la taille de la mémoire principale. Ne sous-estimez pas la qualité de mise en œuvre de l'Unix commande de tri. Le tri a été utilisé pour traiter de très grands volumes de données (pensez à l'origine AT&T les données de facturation) sur les machines avec 128k (c'est 131.072 octets) de mémoire (PDP-11). Lorsque le tri des rencontres plus de données qu'une limite prédéfinie (souvent à l'écoute proche de la taille de la machine principale de mémoire) il trie les données qu'il a lu dans la mémoire principale et l'écrit dans un fichier temporaire. Il répète ensuite l'action avec le côté des blocs de données. Enfin, il effectue une sorte de fusion sur les fichiers intermédiaires. Cela permet de tri de travailler sur des données beaucoup plus important que celui de la machine de la mémoire principale.

4voto

Vinko Vrsalovic Points 116138

Il semble que vous ayez à utiliser une grande quantité de code pour simuler les hachages en bash pour obtenir de comportement linéaire ou un bâton pour les quadratique superlinear versions.

Parmi ces versions, saua's est la meilleure solution (et la plus simple):

sort -n ip_addresses.txt | uniq -c

J'ai trouvé http://unix.derkeiler.com/Newsgroups/comp.unix.shell/2005-11/0118.html. Mais c'est moche comme l'enfer...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X