J'ai un fichier qui augmente d'environ 200 000 lignes par jour, et il est formé de blocs de trois lignes en tant que tels:
1358726575123 # key
Joseph Muller # name
carpenter # job
9973834728345
Andres Smith
student
7836472098652
Mariah Anthony
dentist
Maintenant, j'ai un autre fichier à partir duquel j'extrais environ 10 000 modèles de clés, tels que 1358726575123
. Ensuite, je lance une for
boucle avec ces modèles et je dois les comparer avec le premier fichier. Si le fichier ne contient pas un tel modèle, j'enregistre le modèle dans un troisième fichier pour un traitement ultérieur:
for number in $(grep -o '[0-9]\{12\}' file2); do # finds about 10.000 keys
if ! grep -q ^$number$ file1; then # file1 is a huge file
printf "$number\n" >>file3 # we'll process file3 later
fi
done
L'exemple de code récupère un énorme fichier 10 000 fois et j'exécute cette boucle environ une fois par minute, pendant toute la journée .
Étant donné que l'énorme fichier continue de croître, que puis-je faire pour accélérer tout cela et économiser du processeur? Je me demande si le tri du fichier par sa clé (si oui, comment?) Ou l'utilisation d'une base de données au lieu de texte brut aiderait ...
Réponses:
Cette réponse est basée sur la
awk
réponse postée par potong ..Elle est deux fois plus rapide que la
comm
méthode (sur mon système), pour les mêmes 6 millions de lignes dans le fichier principal et 10 mille clés ... (maintenant mis à jour pour utiliser FNR, NR)Bien qu'il
awk
soit plus rapide que votre système actuel, et vous donnera à vous et à vos ordinateurs un peu de répit, sachez que lorsque le traitement des données est aussi intense que vous l'avez décrit, vous obtiendrez les meilleurs résultats globaux en passant à une base de données dédiée; par exemple. SQlite, MySQL ...la source
file1 -> mainfile
etfile2 -> keys
avec gawk et mawk, et il émet de mauvaises touches.awk
vous permettent de lire dans une série de fichiers .. dans ce cas , cette série a 3 fichiers qu'il contient La sortie va.stdout
mainfile
, ET il imprimera également toutes les clés dukeys
fichier qui ne sont pas dansmainfile
... C'est probablement ce qui se passe ... (Je vais y aller un peu plus loin ...$RANDOM
pour le téléchargement.Le problème, bien sûr, est que vous exécutez grep sur le gros fichier 10 000 fois. Vous ne devez lire les deux fichiers qu'une seule fois. Si vous voulez rester en dehors des langages de script, vous pouvez le faire de cette façon:
comm
sur les listes triées pour obtenir ce qui n'est que sur la deuxième listeQuelque chose comme ça:
Tu vois
man comm
.Si vous pouviez tronquer le gros fichier tous les jours (comme un fichier journal), vous pourriez garder un cache de numéros triés et n'auriez pas besoin de l'analyser en entier à chaque fois.
la source
{12}
<(grep...sort)
emplacement des noms de fichiers.tail -n +$linenum
pour sortir uniquement les dernières données. De cette façon, vous ne traiterez qu'environ 200 000 lignes par jour .. Je viens de le tester avec 6 millions de lignes dans le fichier principal et 10 000 clés ... temps : réel 0m0.016s, utilisateur 0m0.008s, sys 0m0.008sOui, utilisez certainement une base de données. Ils sont faits exactement pour des tâches comme celle-ci.
la source
Cela pourrait fonctionner pour vous:
ÉDITER:
Script modifié pour permettre les doublons et les clés inconnues dans les deux fichiers, produit toujours des clés à partir du premier fichier non présentes dans le second:
la source
Avec autant de données, vous devriez vraiment passer à une base de données. En attendant, une chose que vous devez faire pour obtenir des performances décentes est de ne pas rechercher
file1
séparément chaque clé. Exécutez-en ungrep
pour extraire toutes les clés non exclues à la fois. Étant donné que celagrep
renvoie également les lignes qui ne contiennent pas de clé, filtrez-les.(
-Fx
signifie littéralement rechercher des lignes entières.-f -
signifie lire une liste de modèles à partir d'une entrée standard.)la source
-v
(-Fxv
) peut s'en occuper.comm
.Permettez-moi de renforcer ce que les autres ont dit: "Accédez à une base de données!"
Des binaires MySQL sont disponibles gratuitement pour la plupart des plateformes.
Pourquoi pas SQLite? Il est basé sur la mémoire, charge un fichier plat lorsque vous le démarrez, puis le ferme lorsque vous avez terminé. Cela signifie que si votre ordinateur tombe en panne ou que le processus SQLite disparaît, il en va de même pour toutes les données.
Votre problème ne ressemble qu'à quelques lignes de SQL et s'exécutera en millisecondes!
Après avoir installé MySQL (que je recommande par rapport à d'autres choix), je débourserais 40 $ pour le livre de recettes SQL d'O'Reilly par Anthony Molinaro, qui présente de nombreux modèles de problèmes, commençant par des
SELECT * FROM table
requêtes simples et passant par des agrégats et plusieurs jointures.la source
Je ne sais pas si c'est la sortie exacte que vous recherchez, mais probablement le moyen le plus simple est:
Vous pouvez également utiliser:
Chacun d'eux crée un fichier de motif temporaire qui est utilisé pour extraire les nombres du grand fichier (
file1
).la source
grep -vf
au lieu degrep -f
.Je suis entièrement d'accord avec vous pour obtenir une base de données (MySQL est assez facile à utiliser). Jusqu'à ce que vous exécutiez cela, j'aime la
comm
solution d'Angus , mais tellement de gens essaientgrep
et se trompent que je pensais montrer la (ou au moins une) bonne façon de le fairegrep
.Le premier
grep
récupère les clés. Le troisièmegrep
(dans le<(...)
) prend toutes les clés utilisées dans le gros fichier, et le<(...)
passe comme un fichier comme argument-f
dans le deuxième grep. Cela oblige le secondgrep
à l'utiliser comme une liste de lignes à faire correspondre. Il l'utilise ensuite pour faire correspondre son entrée (la liste des clés) à partir du canal (en premiergrep
), et imprime toutes les clés extraites du fichier de clés et non (-v
) le gros fichier.Bien sûr, vous pouvez le faire avec des fichiers temporaires dont vous devez garder la trace et ne pas oublier de supprimer:
Cela imprime toutes les lignes
allkeys
qui n'apparaissent pas dansusedkeys
.la source
grep: Memory exhausted
comm
, dans cet ordre.Le fichier clé ne change pas? Ensuite, vous devez éviter de rechercher encore et encore les anciennes entrées.
Avec
tail -f
vous pouvez obtenir la sortie d'un fichier en pleine croissance.grep -f lit les motifs dans un fichier, une ligne comme motif.
la source
N'allait pas publier ma réponse parce que je pensais qu'une telle quantité de données ne devrait pas être traitée avec un script shell, et la bonne réponse pour utiliser une base de données était déjà donnée. Mais depuis maintenant, il existe 7 autres approches ...
Lit le premier fichier en mémoire, puis recherche le deuxième fichier pour les nombres et vérifie si les valeurs sont stockées en mémoire. Cela devrait être plus rapide que plusieurs
grep
s, si vous avez suffisamment de mémoire pour charger le fichier entier, c'est-à-dire.la source
Je suis d'accord avec @ jan-steinman que vous devez utiliser une base de données pour ce type de tâche. Il existe de nombreuses façons de pirater une solution avec un script shell comme le montrent les autres réponses, mais le faire de cette façon entraînera beaucoup de misère si vous allez utiliser et maintenir le code pendant une durée supérieure à juste un projet jetable d'une journée.
En supposant que vous êtes sur une boîte Linux, vous avez probablement installé Python par défaut qui inclut la bibliothèque sqlite3 à partir de Python v2.5. Vous pouvez vérifier votre version Python avec:
Je recommande d'utiliser la bibliothèque sqlite3 car c'est une solution simple basée sur des fichiers qui existe pour toutes les plateformes (y compris à l'intérieur de votre navigateur Web!) Et qui ne nécessite pas l'installation d'un serveur. Essentiellement zéro configuration et zéro entretien.
Vous trouverez ci-dessous un simple script python qui analysera le format de fichier que vous avez donné en exemple, puis effectue une simple requête "tout sélectionner" et génère tout ce qu'il a stocké dans la base de données.
Oui, cela signifie que vous devrez apprendre du SQL , mais cela en vaudra la peine à long terme. De plus, au lieu d'analyser vos fichiers journaux, vous pouvez peut-être écrire des données directement dans votre base de données sqlite.
la source
/usr/bin/sqlite3
fonctionne de la même manière pour les scripts shell ( packages.debian.org/squeeze/sqlite3 ), même si je ne l'ai jamais utilisé./usr/bin/sqlite3
avec des scripts shell, mais je recommande d'éviter les scripts shell, sauf pour les programmes simples à jeter et d'utiliser à la place un langage comme python qui a une meilleure gestion des erreurs et est plus facile à maintenir et à développer.