Supposons que vous ayez un fichier contenant des adresses IP, une adresse dans 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 le nombre de fois qu'elle 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 procéder est:
cat ip_addresses |uniq |while read ip
do
echo -n $ip" "
grep -c $ip ip_addresses
done
Mais c'est vraiment loin d'être efficace.
Comment résoudriez-vous ce problème plus efficacement en utilisant bash?
(Une chose à ajouter: je sais que cela peut être résolu depuis perl ou awk, je suis intéressé par une meilleure solution en bash, pas dans ces langues.)
INFORMATION ADDITIONNELLE:
Supposons que le fichier source mesure 5 Go et que la machine exécutant l'algorithme dispose de 4 Go. Le tri n'est donc pas une solution efficace, pas plus que la lecture du fichier plus d'une fois.
J'ai aimé la solution de type table de hachage - n'importe qui peut apporter des améliorations à cette solution?
INFORMATION SUPPLÉMENTAIRE # 2:
Certaines personnes ont demandé pourquoi je prendrais la peine de le faire en bash alors que c'est beaucoup plus facile, par exemple en perl. La raison en est que sur la machine que je devais faire, ce Perl n'était pas disponible pour moi. C'était une machine Linux construite sur mesure sans la plupart des outils auxquels je suis habitué. Et je pense que c'était un problème intéressant.
Alors s'il vous plaît, ne blâmez pas la question, ignorez-la si vous ne l'aimez pas. :-)
Réponses:
Cela imprimera le décompte en premier, mais à part cela, il devrait être exactement ce que vous voulez.
la source
sort ip_addresses | uniq -c | sort -nr
sort ip_addresses | uniq -c | sort -nr | awk '{ print $2, $1 }'
pour obtenir l'adresse IP dans la première colonne et compter dans la seconde.sort -nr -k1,1
La méthode rapide et sale est la suivante:
cat ip_addresses | sort -n | uniq -c
Si vous devez utiliser les valeurs dans bash, vous pouvez affecter la commande entière à une variable bash, puis parcourir les résultats.
PS
Si la commande sort est omise, vous n'obtiendrez pas les résultats corrects car uniq ne regarde que les lignes identiques successives.
la source
pour résumer plusieurs champs, sur la base d'un groupe de champs existants, utilisez l'exemple ci-dessous: (remplacez les $ 1, $ 2, $ 3, $ 4 selon vos besoins)
la source
sort
etuniq
sont plus faciles à faire des comptages, mais ne vous aident pas lorsque vous devez calculer / additionner des valeurs de champs. La syntaxe de tableau awk est très puissante et clé pour le regroupement ici. Merci!print
fonction d'awk semble réduire les entiers de 64 bits à 32 bits, donc pour les valeurs int supérieures à 2 ^ 31, vous voudrez peut-être utiliserprintf
le%.0f
format au lieu deprint
celaarr[$1,$2]+=$3+$4
par exemplearr[$1,$2]=(arr[$1,$2] $3 "," $4). I needed this to provide a grouped-by-package list of files (two columns only) and used:
arr [$ 1] = (arr [$ 1] $ 2) `avec succès.La solution canonique est celle mentionnée par un autre répondant:
Il est plus court et plus concis que ce qui peut être écrit en Perl ou en awk.
Vous écrivez que vous ne souhaitez pas utiliser le tri, car la taille des données est supérieure à la taille de la mémoire principale de la machine. Ne sous-estimez pas la qualité d'implémentation de la commande de tri Unix. Le tri a été utilisé pour gérer de très gros volumes de données (pensez aux données de facturation originales d'AT & T) sur des machines avec 128 Ko (soit 131 072 octets) de mémoire (PDP-11). Lorsque le tri rencontre plus de données qu'une limite prédéfinie (souvent réglée près de la taille de la mémoire principale de la machine), il trie les données qu'il a lues dans la mémoire principale et les écrit dans un fichier temporaire. Il répète ensuite l'action avec les prochains blocs de données. Enfin, il effectue un tri par fusion sur ces fichiers intermédiaires. Cela permet au tri de fonctionner sur des données beaucoup plus volumineuses que la mémoire principale de la machine.
la source
cette commande vous donnerait la sortie souhaitée
la source
Il semble que vous devez utiliser une grande quantité de code pour simuler les hachages dans bash pour obtenir un comportement linéaire ou vous en tenir aux versions super linéaires
quadratiques.Parmi ces versions, la solution de saua est la meilleure (et la plus simple):
J'ai trouvé http://unix.derkeiler.com/Newsgroups/comp.unix.shell/2005-11/0118.html . Mais c'est moche comme l'enfer ...
la source
Solution (regrouper par comme mysql)
Résultat
la source
Vous pouvez probablement utiliser le système de fichiers lui-même comme table de hachage. Pseudo-code comme suit:
En fin de compte, tout ce que vous avez à faire est de parcourir tous les fichiers et d'y imprimer les noms et les numéros de fichier. Alternativement, au lieu de garder un décompte, vous pouvez ajouter un espace ou une nouvelle ligne à chaque fois au fichier, et à la fin, il suffit de regarder la taille du fichier en octets.
la source
Je pense que le tableau associatif awk est également pratique dans ce cas
Un groupe par courrier ici
la source
La plupart des autres solutions comptent les doublons. Si vous avez vraiment besoin de regrouper des paires de valeurs clés, essayez ceci:
Voici mes données d'exemple:
Cela affichera les paires de valeurs clés regroupées par la somme de contrôle md5.
la source
Pur frapper (pas de fourchette!)
Il y a un moyen, en utilisant un frapperfonction . Cette façon est très rapide car il n'y a pas de fourche! ...
... Alors que des tas d' adresses IP restent petites !
Remarque: les adresses IP sont converties en une valeur entière non signée de 32 bits, utilisée comme index pour le tableau . Cela utilise des tableaux bash simples , pas un tableau associatif (ce qui est plus cher)!
Sur mon hôte, cela est beaucoup plus rapide que d'utiliser des fourches, jusqu'à environ 1'000 adresses, mais cela prend environ 1 seconde entière lorsque j'essaierai de trier et de compter 10'000 adresses.
la source
Je l'aurais fait comme ça:
mais uniq pourrait fonctionner pour vous.
la source
Je comprends que vous cherchez quelque chose dans Bash, mais au cas où quelqu'un d'autre chercherait quelque chose en Python, vous pourriez envisager ceci:
Comme les valeurs de l'ensemble sont uniques par défaut et que Python est plutôt bon dans ce domaine, vous pourriez gagner quelque chose ici. Je n'ai pas testé le code, il pourrait donc être buggé, mais cela pourrait vous y amener. Et si vous voulez compter les occurrences, l'utilisation d'un dict au lieu d'un ensemble est facile à mettre en œuvre.
Edit: je suis un lecteur moche, donc j'ai répondu mal. Voici un extrait avec un dict qui compterait les occurrences.
Le dictionnaire mydict contient maintenant une liste d'IP uniques en tant que clés et le nombre de fois où elles se sont produites comme valeurs.
la source
itertools.groupby()
qui, combiné avec,sorted()
fait exactement ce que demande OP.Le tri peut être omis si la commande n'est pas significative
ou
si la liste source est une variable
la source