Comment couper une partie du fichier journal?

18

J'ai un fichier journal de 8 Go (journal de production Rails). J'ai besoin de le couper entre certaines dates (lignes). Quelle commande pourrais-je utiliser pour ce faire?

Eric Leschinski
la source
1
Hé les gars, cette question concerne un gros fichier, donc c'est "Ante up!" ... le temps compte ... J'ai testé le script sed préféré sur un vrai fichier de 8 Go, avec 85904064 lignes (100 caractères par ligne). J'adore sed, mais en l'état, le script sed scanne l'intégralité du fichier, à chaque fois. Cela le rend en moyenne deux fois plus lent que le script awk qui sort quand il est trouvé ... Je pense que (?) Le script sed peut juste avoir besoin de aq au lieu de d pour la deuxième expression ... Les résultats du test sont ici: coller .ubuntu.com / 573477 .. En outre, il ne produit pas la sortie appropriée .. voir mon commentaire à la fin de la réponse de asoundmove.
Peter.O
La nouvelle version sed de asoundmove avait résolu le problème de vitesse, et elle correspond désormais à la vitesse d'awks. et le nouveau versin affiche désormais correctement les données ... voir ses commentaires pour plus de détails.
Peter.O
Je viens de remarquer que vous avez dit "couper" (ce qui signifie généralement supprimer) ... Voulez-vous vraiment dire "couper" ou voulez-vous dire "copier"? .... Si vous vouliez dire "couper", alors vous sedle ferez facilement.
Peter.O

Réponses:

12

Quelque chose comme

sed '1,/last date prior to chunk/d;/first date after chunk/,$d' logfile | tee cut-log | less

tee cut-logvous permet de voir à l'écran ce qui est mis dans le fichier cut-log.

ÉDITER:

Pour satisfaire les normes rigoureuses de fred.bear, voici une solution sed (bien que sans doute la solution awk soit beaucoup plus jolie):

b=BB; e=EE ;echo -e "AA\nAA\nBB\nBB\nCC\nCC\nDD\nDD\nEE\nEE\nFF\nFF" | sed -n ":b;/$b/b p;n;b b;:p;p;n;/$e/b e;b p;:e;p;n;/$e/b e;q"
asoundmove
la source
3
@dogbane: ouais, ouais. Édité. Je suis sûr que vous écrivez parfois moins que du code optimal, mérite-t-il un commentaire aussi dur?
asoundmove
1
Remarque: s'il y a plusieurs lignes de `` première date '' consécutives avec la même date, toutes sauf la première ne seront pas supprimées et seront introduites dans la sortie ... juste quelque chose à savoir ... (cela dépend de la situation)
Peter.O
1
... mais, même si je suis un pro-sed ++, je pense que ce travail particulier est au-delà de ses limites, pour autre chose qu'un outil personnel. Voici le principal problème que sed a dans ce cas (le vôtre et le mien .. J'ai réussi à obtenir sed pour faire la même chose que la vôtre .. il a également fonctionné à moins de 1%) .. retour au problème principal .. (qui ne s'applique pas à awk) .... Bug (non réparable): En ce qui concerne une date qui est valide dans le cadre du journal, mais qui n'est pas réellement présente dans le journal, dans le cas du 1er argument, sed n'imprimera rien, et dans le cas du 2e argument, sed imprimera tout après la première date! ... plus ...
Peter.O
1
Un autre bogue, réparable: est-ce qu'il correspond actuellement aux dates de n'importe quelle ligne, y compris la protion de données, mais ce n'est qu'un regex tweak .. Et pour ceux qui veulent l'utiliser, vous pourriez peut-être commenter que les arguments se réfèrent maintenant au premier et dernières dates dans l'intervalle (pas -1 et +1) .. et enfin .. mes "normes exigeantes" ne sont pas les miennes. Je ne suis que le messager de la questionneurs demande ... L'utilisateur se remarque si elle fonctionne sur demande, ou non .. Cela a été une grande question pour moi .. J'ai appris beaucoup :) ... et je heureux de savoir que cela sedpeut correspondre awkà la vitesse, et c'était en fait un peu plus rapide.
Peter.O
6

Pour tout imprimer entre FOO et BAR inclus, essayez:

$ sed -n '/FOO/,/BAR/p' file.txt
dogbane
la source
1
note: Cela n'imprimera que le premier BAR d'une série de BARS consécutifs ...
Peter.O
une autre note ... Gros problème si aucune des dates n'est présente dans les données .. Si la dernière date n'est pas présente, sed continuera de produire des lignes jusqu'à ce qu'elle atteigne EOF.
Peter.O
5

Cela fera ce que vous voulez ...
Les dates d'inclusion et d'exclusion sont affichées.

# set Test args
set  2011-02-24  2011-02-26  "junk"

from="$1"
till="$2"
file="$3"

# EITHER ====                              +++++++++  
# Ouptut lines between two parameter dates INCLUDING the parameter dates
  awk -v from=$from -v till=$till '
    ($2 >= from) && ($2 <= till) { print $0 ; next }
    ($2 > till) { exit }' "$file"

# OR ========                              ---------
# Ouptut lines between two parameter dates EXCLUDING the parameter dates
  awk -v from=$from -v till=$till '
    ($2 > from) && ($2 < till) { print $0 ; next }
    ($2 >= till) { exit }' "$file"

Il teste une date (triée) dans le champ 2 ... Voici un exemple des données de test

    98  2011-02-05 xxxx
    99  2011-02-05 xxxx
   100  2011-02-06 xxxx
   101  2011-02-06 xxxx

Et voici le générateur de données de test .

Peter.O
la source
Je l' awk -v from="$from" -v till="$till" '($2 >= from) { if ($2 <= till) { print } else { exit }' "$file"
écrirais
@asoundmove: Oui, cela peut sembler mieux, et c'est certainement plus conventionnel , mais en réalité, son temps d'exécution n'est que la durée d'une ifinstruction supplémentaire au total (pas même 1 par ligne), c'est-à-dire. le flux logique est effectivement le même, et la différence de temps d'exécution serait comptée en nanosecondes .... La seule raison pour laquelle je n'ai pas utilisé "else" est que c'est effectivement mon tout premier awkscript (à part un jour 4 ans il y a quand j'ai joué avec quelques exemples) ... et c'est le premier mécanisme de branche réalisable que j'ai trouvé ... (et comme mentionné. c'est tout aussi rapide) .. J'utilise généreusement sedTryq
Peter.O
Je ne comprends pas où vous donnez le nom et l'emplacement du fichier texte dans cette méthode? quelqu'un peut-il m'aider à voir à travers ma stupidité
Giles
4

Si dans votre fichier journal vous avez les dates dans ce format YYYY-MM-DD, alors, pour trouver toutes les entrées pour disons, 2011-02-10, vous pouvez faire:

grep 2011-02-10 log_file

Maintenant, disons, si vous voulez trouver les entrées pour 2011-02-10 et 2011-02-11, utilisez à nouveau grepmais avec plusieurs modèles:

grep -E '2011-02-10|2011-02-11' log_file
Barun
la source
Bien. Cela fonctionne "comme annoncé" :) ... Cependant, greprecherchera tout le fichier, même si la plage de dates est au début du fichier. En moyenne, cela double le temps de recherche, par rapport à "exit-after-last-item-in-range" ... Je ne prends la peine de le mentionner qu'en raison de la taille de fichier de 8 Go mentionnée dans la question, Votre les résultats de temps de grep sont presque identiques à l'exemple sed ici (1min 58sec). Voici le lien vers mes résultats de tests de temps: paste.ubuntu.com/573477
Peter.O
1

Travailler avec cette taille de fichiers est toujours difficile.

Un moyen d'aller de l'avant pourrait être de diviser ce fichier en deux petits, pour ce faire, vous pouvez utiliser la commande de fractionnement.

split -d -l 50000 ToBigFile.data file_

Même si elle est divisée, vous pouvez toujours travailler avec le fichier comme s'il s'agissait d'une boucle bash for

for f in `ls file_*`; do cat $f; done;

Mais au lieu du chat, vous pouvez utiliser la grep inversée pour vous débarrasser des données indésirables, ce qui n'est pas pertinent pour cela. (ou le type de raffinement dont vous avez besoin).

À ce stade, vous ne travaillerez qu'avec de nombreux fichiers plus petits, et les commandes mentionnées ci-dessus fonctionneront mieux sur de nombreux fichiers plus petits.

Et lorsque vous avez terminé, vous pouvez utiliser une seconde boucle for pour reconstituer le nouveau fichier plus petit.

for f in `ls file_*`; do cat $f >> NewFile.data ; done;

Mise à jour Depuis que nous commençons à diviser les données en plusieurs fichiers, il va y avoir beaucoup de travail avec le disque dur et cela prend du temps. (Dans cette question apparemment 5min).

D'un autre côté, les prochaines étapes seraient probablement plus rapides.

Donc, cette méthode est probablement inutile pour une opération grep, awk, sed simple, mais si les modèles de recherche deviennent plus compliqués, ils pourraient devenir plus rapides.

Johan
la source
3
Johanm, il ne faut pas plus de 1 minute, en moyenne, pour rechercher un fichier journal de 8 Go sur mon ordinateur, et sur le même ordinateur, juste le fractionnement initial du fichier, cela prend 4min 43sec ... :)
Peter.O
Disons que vous pouvez réduire ces temps awk et sed de 50% sur les fichiers plus petits. Ensuite, nous devons encore faire plus de 10 de ces opérations avant de gagner sur le temps total ... Alors peut-être que le fractionnement de fichiers n'est pas la meilleure idée pour quelques régressions ...
Johan
Le script awk pourrait (facilement) être modifié pour produire 10 résultats de recherche différents dans 10 fichiers ... en un seul passage, mais cela ralentirait la lecture tout en produisant les rapports ... Sed pourrait également faire la même chose, mais comme je 'ai mentionné dans les commentaires de asoundmove, sed échouera si une date / heure particulière n'a pas d'entrée dans le journal (par exemple, vous recherchez par heure) .. J'utilise beaucoup sed et c'est extrêmement utile, mais il a ses limites ... Voici une FAQ sed sur quand utiliser sed vs awk .. Je ne suis pas nécessairement d'accord avec tout cela, mais je peux voir ce qu'ils signifient ... sed.sourceforge.net/sedfaq6.html
Peter. O
0
perl -wlne '/^2011-02-24/ .. /^2011-02-25/ and print' log_file
Le pont Charles
la source
Cependant, cela n'imprimera que la première entrée du journal pour le 25/02/2011.
Gilles 'SO- arrête d'être méchant'