Я ищу версию grep
с малой сложностью для Python, в частности, grep -C <context>
(или, альтернативно, grep -B <before-context> -A <after-context>
).
У меня есть модуль поиска grep, соответствующий только что найденной строке.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#@file bbd025_pygrep.py
import re
def grep(string_to_find, filename):
the_result_str = ''
with open(filename, 'r') as f:
lines = f.readlines()
for line in lines:
if re.match(string_to_find, line):
the_result_str += line + "\n"
return the_result_str
##endof: grep(string_to_find, filename)
Моя ситуация сейчас такова, что у меня есть файл длиной 1386257 строк - лог-файл для записей базы данных. Я анализирую его для сбора различной информации об одной записи в базе данных за раз. На данный момент мой процесс состоит в grep
одной строке, такой как
Feb 29 10:06:37 - [159|x|x|x|x|x],[x|x|x|x],DB_INSERT,DB_ID=137
Используя значение 159
, я выполняю четыре других grep
s, чтобы получить всю необходимую информацию о записи в базе данных.
Проблема в том, что при более чем 1 Mline пять отдельных grep
стоят дорого. Одна приятная вещь - для каждой записи есть только одна строка, такая как предыдущая. Однако остальные четыре строки не всегда располагаются одинаково относительно первой. Если я использую сокращение, то указанная выше строка обозначается
line2_entry137
и еще одна строка, соответствующая той же записи
Feb 29 10:06:37 - [159|15|x|x|x|x],[unknown|x|7|1.2.3.4],ACTION_BEGIN,USER_INFO
обозначается
line3_entry137
Я могу получить такие ситуации, как
line1_entry137
line5_entry135
line2_entry137
line3_entry136
line3_entry137
line1_entry138
line2_entry138
line4_entry137
line3_entry138
line4_entry138
line4_entry136
line5_entry138
line5_entry138
line5_entry137
Выше для краткости я показал, что есть также line1
, line4
и line5
для entry137
. Это для пяти grep
с. В действительности, существуют также значения от line6
до line20
, которые соответствуют моему значению 159
и соответствуют entry137
.
Однако я определил, что в подавляющем большинстве случаев (я надеюсь, что во всех случаях) вся информация для entry137
находится в пределах 50 строк от line2
.
Если бы я просто использовал bash
(что я не могу - вещи компании), я мог бы радикально сократить время поиска. Я покажу это с помощью ряда команд (где я не включаю весь вывод.)
$ time grep "DB_INSERT\,DB_ID\=137$" /my/path/logfile.log
Feb 29 10:06:37 - [159|x|x|x|x|x],[x|x|x|x],DB_CALL_INSERT,DB_ID=137
real 0m1.708s
user 0m0.078s
sys 0m0.046s
# because such a line only appears once
$ time grep -m 1 "DB_INSERT\,DB_ID\=137$" /my/path/logfile.log
Feb 29 10:06:37 - [159|x|x|x|x|x],[x|x|x|x],DB_CALL_INSERT,DB_ID=137
real 0m0.171s
user 0m0.000s
sys 0m0.046s
$ time grep -m 1 -B 50 -A 50 "DB_INSERT\,DB_ID\=137$" /my/path/logfile.log > /dev/null
real 0m0.157s
user 0m0.031s
sys 0m0.030s
Я знаю, что могу реализовать деталь -m 1
, используя break
из моего for line in lines
цикла. Однако я не знаю, как реализовать команду -A
или -B
(или, что то же самое, -C
) в Python. Я не знаю, как "вернуться" или "опередить" линию, на которой я нахожусь.
Основные шаги после этого будут следующие:
$ pertinent_line=$(grep -m 1 "DB_INSERT\,DB_ID\=137$" /my/path/logfile.log)
# parse out the date and the '159'
$ pertinent_stuff=$(grep -m 1 -C 50 "DB_INSERT\,DB_ID\=137$" /my/path/logfile.log)
$ info3=$(echo "${pertinent_stuff}" | grep "^Feb 29.*\[159.*USER_INFO$"
$ echo "$(info3)"
Feb 29 10:06:37 - [159|15|x|x|x|x],[unknown|x|7|1.2.3.4],ACTION_BEGIN,USER_INFO
$ # etc. with greps for the other 3 lines
Как мне добавить context
материал в мой Python grep
?
P.S. Моя работа над этим заключалась в том, чтобы выяснить, как реализовать grep
в Python, как это видно. Я не уверен, куда пойти для отдыха.