Comment obtenir du texte à partir d'une plage de dates en utilisant grep / sed dans un grand fichier texte?

9

J'ai un gros fichier texte (près de 3 Go) - c'est un fichier journal. Je veux obtenir des lignes de texte qui correspondent à une plage de dates de ce fichier, du 13 juillet au 19 juillet. Mon format de journal est:

2016-07-12 < ?xml version>
2016-07-13 < ?xml version>
2016-07-18 < ?xml version>
2016-07-18 < ?xml version>
2016-07-19 < ?xml version>
2016-07-20 < ?xml version>
sample text sample text
sample text sample text
sample text sample text
2016-07-20 < ?xml version>
sample text sample text
2016-07-20 < ?xml version>

donc après grep/ sedil devrait être sorti comme ceci:

2016-07-13 < ?xml version>
2016-07-18 < ?xml version>
2016-07-18 < ?xml version>
2016-07-19 < ?xml version>

Comment puis-je l'obtenir?

corey
la source
2
Êtes-vous sûr de vouloir dire juin ? Toutes les dates de votre exemple de fichier journal sont en juillet et l'échantillon de sortie souhaité implique que vous vouliez ce dernier.
David Foerster

Réponses:

13

Avec grepsi vous connaissez le nombre de lignes que vous voulez, vous pouvez utiliser l'option contextuelle -Apour imprimer des lignes après le motif

grep -A 3 2016-07-13 file

qui vous donnera la ligne avec 2013-07-13 et les 3 lignes suivantes

avec sedvous pouvez utiliser les dates pour délimiter comme ceci

sed -n '/2016-07-13/,/2016-07-19/p' file

qui imprimera toutes les lignes de la première ligne avec 2016-07-13 jusqu'au et y compris la première ligne avec 2016-07-19. Mais cela suppose que vous n'avez qu'une seule ligne avec 2016-07-19 (il n'imprimera pas la ligne suivante). S'il y a plusieurs lignes, utilisez la date suivante à la place et utilisez dpour en supprimer la sortie

sed -n '/2016-07-13/,/2016-07-20/{/2016-07-20/d; p}' file
Zanna
la source
11

Ce simple liner grep one suffira:

grep -E ^2016-07-1[3-9] filename

Fonctionne bien ici et il n'y a pas besoin de sed :)

Références:

andrew.46
la source
1
Comme toujours, vous apportez la grâce :)
Zanna
(y) ... a dû retirer ^pour que cela fonctionne. Utilisation de Mac.
Anum Sheraz
4

awk Solution:

$ awk '/^2016-07-13.*/,/2016-07-19.*/'  input.txt                                   
2016-07-13 < ?xml version> 
2016-07-18 < ?xml version> 
2016-07-18 < ?xml version> 
2016-07-19 < ?xml version> 

Imprime essentiellement n'importe quelle ligne de celle qui commence par 2016-07-13celle qui commence par2016-07-19

Sergiy Kolodyazhnyy
la source
4

Toutes les autres réponses actuelles reposent sur le fait que les entrées du fichier journal sont triées chronologiquement ou sur le fait que la plage de dates peut être facilement mise en correspondance avec des expressions régulières. Si vous voulez une solution plus générique, nous devons faire plus de programmation.

Je présente ce script GNU AWK:

#!/usr/bin/gawk -f
BEGIN {
    starttime = mktime(starttime)
    endtime = mktime(endtime)
}

func in_range(n, start, end) {
    return start <= n && n < end
}

match($0, /^([0-9]{4})-([0-9]{2})-([0-9]{2})\s/, m) &&
    in_range(mktime(m[1] " " m[2] " " m[3] " 00 00 00"), starttime, endtime)

Vous fournissez l'heure de début et de fin via les variables starttimeet endtimedans un format qui mktimecomprend ( YYYY MM DD hh dd ss). Ainsi, vous exécutez la awkcommande comme ceci, en supposant que le script Awk ci-dessus se trouve dans un fichier exécutable filter-log-dates.awkdans le répertoire de travail actuel et que le fichier journal est mylog.txt:

./filter-log-dates.awk -v starttime='2016 07 13 00 00 00' -v endtime='2016 07 20 00 00 00' mylog.txt

Notez que l'heure de fin est exclusive , c'est-à-dire que les enregistrements de journal valides doivent avoir un horodatage avant l'heure de fin.

Si votre format d'horodatage est différent, vous pouvez ajuster l'expression régulière transmise à la matchfonction pour l'adapter.

David Foerster
la source
3

Vous pouvez le faire par étapes. Trouvez le numéro de la première ligne correspondant à votre modèle de départ. Trouvez le numéro de la dernière ligne correspondant à votre motif de fin. Extrayez ensuite le test entre ces deux lignes. Cela peut être fait comme suit.

grep -n 2016-07-13 bigtextfile | head -1
grep -n 2016-07-19 bigtestfile | tail -1
# Say the first number is 1234 and the second 5678, then use...
awk 'NR>=1234 && NR<=5678' bigtestfile > rangeoftext

Cela pourrait être fait tout dans une awkcommande mais les étapes peuvent le rendre plus facile à suivre. Dans awk, la variable NR est le numéro de ligne actuel, et comme aucune action n'a été spécifiée après le modèle (NR> = 1234 && NR <= 5678), l'action par défaut consiste à imprimer les lignes de cette plage.

Jeffrey Ross
la source