Как получить текст из диапазона дат, используя grep/sed в большом текстовом файле?
У меня большой текстовый файл (почти 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>
Как я могу получить это?
5 ответов
С 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
Этого простого grep одного лайнера будет достаточно:
grep -E ^2016-07-1[3-9] filename
Хорошо работает здесь и нет необходимости в седе:)
Рекомендации:
Все остальные текущие ответы основаны на том факте, что записи файла журнала отсортированы в хронологическом порядке или на том факте, что диапазон дат можно легко сопоставить с регулярными выражениями. Если вам нужно более общее решение, нам нужно заняться программированием.
Я представляю этот сценарий 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
функция, чтобы удовлетворить это.
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
Вы можете сделать это поэтапно. Найдите номер первой строки, соответствующий вашему стартовому шаблону. Найдите номер последней строки, соответствующий вашему шаблону окончания. Затем извлеките тест между этими двумя строками. Это можно сделать следующим образом.
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), по умолчанию выполняется печать строк, находящихся в этом диапазоне.