Как получить текст из диапазона дат, используя grep / sed в большом текстовом файле?

9

У меня большой текстовый файл (почти 3 ГБ) - это файл журнала. Я хочу получить строки текста, которые соответствуют диапазону дат из этого файла, с 13 июля по 19 июля. Мой формат журнала:

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>

поэтому после grep/ sedэто должно быть выведено так:

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

Как я могу получить это?

Corey
источник
2
Вы уверены, что имеете в виду июнь ? Все даты в вашем примере файла журнала указаны в июле, и желаемый выходной пример подразумевает, что вы имели в виду последнее.
Дэвид Фёрстер

Ответы:

13

С , grepесли вы знаете количество строк вы хотите , вы можете использовать опцию контекстной -Aпечатать строки после шаблона

grep -A 3 2016-07-13 file

что даст вам строку с 2013-07-13 и следующие 3 строки

с помощью которых sedвы можете использовать даты, чтобы разграничить, как это

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

который напечатает все строки от первой строки с 2016-07-13 до и включая первую строку с 2016-07-19. Но это предполагает, что у вас есть только одна строка с 2016-07-19 (она не будет печатать следующую строку). Если есть несколько строк, используйте следующую дату и dудалите выходные данные

sed -n '/2016-07-13/,/2016-07-20/{/2016-07-20/d; p}' file
Занна
источник
11

Этого простого grep одного лайнера будет достаточно:

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

Хорошо работает здесь и нет необходимости в седе :)

Ссылки:

andrew.46
источник
1
Как всегда, вы приносите благодать :)
Zanna
(у) ... пришлось удалить, ^чтобы заставить его работать. Используя Mac.
Анум Шераз
4

awk решение:

$ 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> 

В основном печатает любую строку от той, которая начинается с 2016-07-13той, которая начинается с2016-07-19

Сергей Колодяжный
источник
4

Все остальные текущие ответы основаны на том факте, что записи файла журнала отсортированы в хронологическом порядке или на том факте, что диапазон дат можно легко сопоставить с регулярными выражениями. Если вам нужно более общее решение, нам нужно заняться программированием.

Я представляю этот скрипт 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)

Вы указываете время начала и окончания через переменные starttimeи endtimeв формате, который mktimeпонимает ( YYYY MM DD hh dd ss). Таким образом, вы запускаете awkкоманду следующим образом, предполагая, что приведенный выше скрипт Awk находится в исполняемом файле filter-log-dates.awkв текущем рабочем каталоге, а файл журнала mylog.txt:

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

Обратите внимание, что время окончания является исключительным , то есть допустимые записи журнала должны иметь отметку времени до времени окончания.

Если формат вашей метки времени отличается, вы можете отрегулировать регулярное выражение, переданное matchфункции, в соответствии с ней.

Дэвид Фёрстер
источник
3

Вы можете сделать это поэтапно. Найдите номер первой строки, соответствующий вашему стартовому шаблону. Найдите номер последней строки, соответствующий вашему шаблону окончания. Затем извлеките тест между этими двумя строками. Это можно сделать следующим образом.

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

Это может быть сделано все в awkкоманде, но шаги могут облегчить следовать. В awk переменная NR является текущим номером строки, и поскольку после шаблона не было задано никаких действий (NR> = 1234 && NR <= 5678), действием по умолчанию является печать строк, находящихся в этом диапазоне.

Джеффри Росс
источник