Como obter texto do intervalo de datas usando grep / sed em um arquivo de texto grande?

9

Tenho texto de arquivo grande (quase 3 GB) - é um arquivo de log. Desejo obter linhas de texto que correspondam a um intervalo de datas deste arquivo, de 13 a 19 de julho. Meu formato de log é:

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>

então after grep/ sedit deve ser produzido assim:

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

Como posso conseguir isso?

Corey
fonte
2
Você tem certeza que quer dizer junho ? Todas as datas em seu arquivo de log de amostra são em julho e a amostra de saída desejada implica que você quis dizer o último.
David Foerster

Respostas:

13

Com grepse você souber o número de linhas que deseja, poderá usar a opção de contexto -Apara imprimir linhas após o padrão

grep -A 3 2016-07-13 file

que lhe dará a linha com 13/07/2013 e as próximas 3 linhas

com sedvocê pode usar as datas para delimitar assim

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

que imprimirá todas as linhas da primeira linha com 13/07/2016 até e inclusive a primeira linha com 19/07/2016. Mas isso pressupõe que você tenha apenas uma linha com 19/07/2016 (não será impressa a próxima linha). Se houver várias linhas, use a próxima data e dexclua a saída dela

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

Este grep one liner simples será suficiente:

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

Funciona bem aqui e não há necessidade de sed :)

Referências:

andrew.46
fonte
1
Como sempre você traz graça :)
Zanna
(y) ... teve que remover ^para fazê-lo funcionar. Usando o Mac.
Anum Sheraz 13/01
4

awk solução:

$ 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 basicamente qualquer linha, desde a que começa 2016-07-13até a que começa com2016-07-19

Sergiy Kolodyazhnyy
fonte
4

Todas as outras respostas atuais se baseiam no fato de que as entradas do arquivo de log são classificadas cronologicamente ou no fato de que o período pode ser facilmente correspondido com expressões regulares. Se você deseja uma solução mais genérica, precisamos fazer mais programação.

Apresento este 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)

Você fornece o horário de início e término através das variáveis starttimee endtimeem um formato que mktimeentende ( YYYY MM DD hh dd ss). Portanto, você executa o awkcomando da seguinte maneira, assumindo que o script Awk acima esteja em um arquivo executável filter-log-dates.awkno diretório de trabalho atual e o arquivo de log seja mylog.txt:

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

Observe que o horário final é exclusivo , ou seja, os registros de log válidos devem ter um carimbo de data e hora antes do horário final.

Se o formato do carimbo de data / hora for diferente, você poderá ajustar a expressão regular passada para a matchfunção para adequá-la.

David Foerster
fonte
3

Você poderia fazer isso em etapas. Encontre o número da primeira linha que corresponde ao seu padrão inicial. Encontre o número da última linha correspondente ao seu padrão final. Em seguida, extraia o teste entre essas duas linhas. Isso pode ser feito da seguinte forma.

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

Isso pode ser feito em um awkcomando, mas as etapas podem facilitar a execução. No awk, a variável NR é o número da linha atual e, como nenhuma ação foi especificada após o padrão (NR> = 1234 && NR <= 5678), a ação padrão é imprimir as linhas nesse intervalo.

Jeffrey Ross
fonte