2013-03-26 1 views
1

У меня есть несколько файлов журналов неоднородная в формате следующим образом:регулярное выражение матч в лог-файл, вернуть динамическое содержание выше и ниже матча

timestamp event summary 
foo details 
account name: userA 
bar more details 
timestamp event summary 
baz details 
account name: userB 
qux more details 
timestamp etc. 

Я хотел бы найти файл журнала для userB, и если найдены , эхо от предыдущей метки времени до (но не включая) следующей метки времени. Вероятно, будет несколько событий, соответствующих моему поиску. Было бы неплохо повторить примерно --- start --- и --- end ---, окружающих каждый матч.

Это было бы идеально для pcregrep -M, правильно? Проблема в том, что pcregrep GnuWin32 сбой с многострочным регулярным выражением, ищущим большие файлы, и эти лог-журналы могут быть 100 мегабайт или более.

То, что я пытался

Мой хаком обходной путь до сих пор включает в себя использование grep -B15 -A30, чтобы найти соответствующие строки и печати окружающего содержимого, а затем конвейеру теперь более управляемым кусок в pcregrep для полировки. Проблема в том, что некоторые события составляют менее десяти строк, а другие - 30 или более; и я получаю неожиданные результаты, когда встречаются более короткие события.

:parselog <username> <logfile> 

set silent=1 
set count=0 
set deez=20\d\d-\d\d-\d\d \d\d:\d\d:\d\d 
echo Searching %~2 for records containing %~1... 

for /f "delims=" %%I in (
    'grep -P -i -B15 -A30 ":\s+\b%~1\b(@mydomain\.ext)?$" "%~2" ^| pcregrep -M -i "^%deez%(.|\n)+?\b%~1\b(@mydomain\.ext|\r?\n)(.|\n)+?\n%deez%" 2^>NUL' 
) do (
    echo(%%I| findstr "^20[0-9][0-9]-[0-9][0-9]-[0-9][0-9].[0-9][0-9]:[0-9][0-9]:[0-9][0-9]" >NUL && (
     if defined silent (
      set silent= 
      set found=1 
      set /a "count+=1" 
      echo; 
      echo ---------------start of record !count!------------- 
     ) else (
      set silent=1 
      echo ----------------end of record !count!-------------- 
      echo; 
     ) 
    ) 
    if not defined silent echo(%%I 
) 

goto :EOF 

Есть ли лучший способ сделать это? Я наткнулся на команду awk, который выглядел интересно, что-то вроде:

awk "/start pattern/,/end pattern/" logfile 

... но это нужно будет соответствовать среднему шаблону, а также. К сожалению, я не знаком с синтаксисом awk. Какие-либо предложения?

Эд Мортон предположил, что я поставляю несколько примеров регистрации и ожидаемого вывода.

Пример поймать-все

2013-03-25 08:02:32 Auth.Critical 169.254.8.110 Mar 25 08:02:32 dc3 MSWinEventLog 2 Security 11730158 Mon Mar 25 08:02:28 2013 529 Security NT AUTHORITY\SYSTEM N/A Audit Failure dc3 2 Logon Failure: 

    Reason:  Unknown user name or bad password 

    User Name: user5f 

    Domain:  MYDOMAIN 

    Logon Type: 3 

    Logon Process: Advapi 

    Authentication Package: Negotiate 

    Workstation Name: dc3 

    Caller User Name: dc3$ 

    Caller Domain: MYDOMAIN 

    Caller Logon ID: (0x0,0x3E7) 

    Caller Process ID: 400 

    Transited Services: - 

    Source Network Address: 169.254.7.86 

    Source Port: 40838 
2013-03-25 08:02:32 Auth.Critical 169.254.8.110 Mar 25 08:02:32 dc3 MSWinEventLog 2 Security 11730159 Mon Mar 25 08:02:29 2013 680 Security NT AUTHORITY\SYSTEM N/A Audit Failure dc3 9 Logon attempt by: MICROSOFT_AUTHENTICATION_PACKAGE_V1_0 

Logon account: USER6Q 

Source Workstation: dc3 

Error Code: 0xC0000234 
2013-03-25 08:02:32 Auth.Critical 169.254.8.110 Mar 25 08:02:32 dc3 MSWinEventLog 2 Security 11730160 Mon Mar 25 08:02:29 2013 539 Security NT AUTHORITY\SYSTEM N/A Audit Failure dc3 2 Logon Failure: 

    Reason:  Account locked out 

    User Name: [email protected] 

    Domain: MYDOMAIN 

    Logon Type: 3 

    Logon Process: Advapi 

    Authentication Package: Negotiate 

    Workstation Name: dc3 

    Caller User Name: dc3$ 

    Caller Domain: MYDOMAIN 

    Caller Logon ID: (0x0,0x3E7) 

    Caller Process ID: 400 

    Transited Services: - 

    Source Network Address: 169.254.7.89 

    Source Port: 55314 
2013-03-25 08:02:32 Auth.Notice 169.254.5.62 Mar 25 08:36:38 DC4.mydomain.tld MSWinEventLog 5 Security 201326798 Mon Mar 25 08:36:37 2013 4624 Microsoft-Windows-Security-Auditing  N/A Audit Success DC4.mydomain.tld 12544 An account was successfully logged on. 

Subject: 
    Security ID:  S-1-0-0 
    Account Name:  - 
    Account Domain:  - 
    Logon ID:  0x0 

Logon Type:   3 

New Logon: 
    Security ID:  S-1-5-21-606747145-1409082233-725345543-160838 
    Account Name:  DEPTACCT16$ 
    Account Domain:  MYDOMAIN 
    Logon ID:  0x1158e6012c 
    Logon GUID:  {BCC72986-82A0-4EE9-3729-847BA6FA3A98} 

Process Information: 
    Process ID:  0x0 
    Process Name:  - 

Network Information: 
    Workstation Name: 
    Source Network Address: 169.254.114.62 
    Source Port:  42183 

Detailed Authentication Information: 
    Logon Process:  Kerberos 
    Authentication Package: Kerberos 
    Transited Services: - 
    Package Name (NTLM only): - 
    Key Length:  0 

This event is generated when a logon session is created. It is generated on the computer that was accessed. 

The subject fields indicate... 
2013-03-25 08:02:32 Auth.Critical 169.254.8.110 Mar 25 08:02:32 dc3 MSWinEventLog 2 Security 11730162 Mon Mar 25 08:02:30 2013 675 Security NT AUTHORITY\SYSTEM N/A Audit Failure dc3 9 Pre-authentication failed: 

    User Name: USER8Y 

    User ID:  %{S-1-5-21-606747145-1409082233-725345543-3904} 

    Service Name: krbtgt/MYDOMAIN 

    Pre-Authentication Type: 0x0 

    Failure Code: 0x19 

    Client Address: 169.254.87.158 
2013-03-25 08:02:32 Auth.Critical etc. 

Пример команды

call :parselog user6q \\path\to\catch-all.log 

Ожидаемый результат

---------------start of record 1------------- 
2013-03-25 08:02:32 Auth.Critical 169.254.8.110 Mar 25 08:02:32 dc3 MSWinEventLog 2 Security 11730159 Mon Mar 25 08:02:29 2013 680 Security NT AUTHORITY\SYSTEM N/A Audit Failure dc3 9 Logon attempt by: MICROSOFT_AUTHENTICATION_PACKAGE_V1_0 

Logon account: USER6Q 

Source Workstation: dc3 

Error Code: 0xC0000234 
---------------end of record 1------------- 


---------------start of record 2------------- 
2013-03-25 08:02:32 Auth.Critical 169.254.8.110 Mar 25 08:02:32 dc3 MSWinEventLog 2 Security 11730160 Mon Mar 25 08:02:29 2013 539 Security NT AUTHORITY\SYSTEM N/A Audit Failure dc3 2 Logon Failure: 

    Reason:  Account locked out 

    User Name: [email protected] 

    Domain: MYDOMAIN 

    Logon Type: 3 

    Logon Process: Advapi 

    Authentication Package: Negotiate 

    Workstation Name: dc3 

    Caller User Name: dc3$ 

    Caller Domain: MYDOMAIN 

    Caller Logon ID: (0x0,0x3E7) 

    Caller Process ID: 400 

    Transited Services: - 

    Source Network Address: 169.254.7.89 

    Source Port: 55314 
---------------end of record 2------------- 
+0

Никогда не используйте 'awk '/ start pattern /,/end pattern /" logfile'. Это делает тривиальный материал немного более кратким, но вы не можете его расширить, чтобы работать на нетривиальные вещи. Если вы разместили образец ввода (я предполагаю, что у вас есть фактические временные метки в вашем файле, а не слово «timestamp») и ожидаемый результат, который поможет. Существует простое решение awk. –

ответ

1

Это все, что вам нужно с GNU AWK (для IGNORECASE):

$ cat tst.awk 
function prtRecord() { 
    if (record ~ regexp) { 
     printf "-------- start of record %d --------%s", ++numRecords, ORS 
     printf "%s", record 
     printf "--------- end of record %d ---------%s%s", numRecords, ORS, ORS 
    } 
    record = "" 
} 
BEGIN{ IGNORECASE=1 } 
/^[[:digit:]]+-[[:digit:]]+-[[:digit:]]+/ { prtRecord() } 
{ record = record $0 ORS } 
END { prtRecord() } 

или с любым AWK:

$ cat tst.awk 
function prtRecord() { 
    if (tolower(record) ~ tolower(regexp)) { 
     printf "-------- start of record %d --------%s", ++numRecords, ORS 
     printf "%s", record 
     printf "--------- end of record %d ---------%s%s", numRecords, ORS, ORS 
    } 
    record = "" 
} 
/^[[:digit:]]+-[[:digit:]]+-[[:digit:]]+/ { prtRecord() } 
{ record = record $0 ORS } 
END { prtRecord() } 

В любом случае вы бы запустить его на UNIX как:

$ awk -v regexp=user6q -f tst.awk file 

Я не знаю синтаксиса Windows, но я ожидаю, что он очень похож, если не идентичен.

Обратите внимание на использование tolower() в скрипте, чтобы сделать обе стороны нижнего регистра сравнения, чтобы совпадение было нечувствительным к регистру. Если вы можете вместо этого переходить в регулярное выражение поиска, это правильный случай, тогда вам не нужно вызывать tolower() по обе стороны от сравнения. nbd, это может немного ускорить скрипт.

$ awk -v regexp=user6q -f tst.awk file 
-------- start of record 1 -------- 
2013-03-25 08:02:32 Auth.Critical 169.254.8.110 Mar 25 08:02:32 dc3 MSWinEventLog 2 Security 
    11730159 Mon Mar 25 08:02:29 2013 680 Security NT AUTHORITY\SYSTEM N/A Audit Failure 
dc3 9 Logon attempt by: MICROSOFT_AUTHENTICATION_PACKAGE_V1_0 

Logon account: USER6Q 

Source Workstation: dc3 

Error Code: 0xC0000234 
--------- end of record 1 --------- 

-------- start of record 2 -------- 
2013-03-25 08:02:32 Auth.Critical 169.254.8.110 Mar 25 08:02:32 dc3 MSWinEventLog 2 Security 
    11730160 Mon Mar 25 08:02:29 2013 539 Security NT AUTHORITY\SYSTEM N/A Audit Failure 
dc3 2 Logon Failure: 

    Reason:  Account locked out 

    User Name: [email protected] 

    Domain: MYDOMAIN 

    Logon Type: 3 

    Logon Process: Advapi 

    Authentication Package: Negotiate 

    Workstation Name: dc3 

    Caller User Name: dc3$ 

    Caller Domain: MYDOMAIN 

    Caller Logon ID: (0x0,0x3E7) 

    Caller Process ID: 400 

    Transited Services: - 

    Source Network Address: 169.254.7.89 

    Source Port: 55314 
--------- end of record 2 --------- 
+0

Это то, чего я ожидал, но на практике это очень медленно. Я думаю, что я хотел бы придерживаться своей идеи использования «grep» с контекстом, а затем обрезать жир с середины, так как «grep», похоже, может найти все совпадения в 100-мегабайтном файле всего за несколько секунд. Тем не менее, 'awk', похоже, повторяет все, что он получил через stdin из' grep'. Интересно, не ведет ли gnuwin32 'awk' то же самое, что и POSIX. Единственные вещи, которые я изменил в 'tst.awk', добавляли регулярное выражение timestamp ('/^ \ d \ d \ d \ d- \ d \ d- \ d \ d \ d \ d: \ d \ d: \ d \ d/'вместо'/timestamp/') и добавил' IGNORECASE = 1' в 'prtRecord()' – rojo

+0

\ d - это сокращение, которое поймут только некоторые инструменты. Вероятно, вы создали единую запись содержимого всего файла, и поэтому она была медленной (конкатенация строк медленнее, чем ввод/вывод в awk, и чем больше строк, тем медленнее она). Используйте классы символов POSIX, такие как [[: digit:]], см. Мой скрипт. Я не верю, что скрипт awk, который я опубликовал, будет медленным. –

+0

IGNORECASE - расширение GNU awk, недоступное в POSIX awks. Если у вас есть GNU awk ('awk -version' скажет вам), вы должны назначить IGNORECASE = 1 в разделе BEGIN, а не в функции, и избавиться от tolower() s. Я обновлю свой скрипт, чтобы показать эту альтернативу. –

0

Я думаю AWK все, что вам нужно:

awk "/---start of record---/,/---end of record---/ {print}" logfile 

Это все, что вам нужно, если первый индикатор линии:

---start of record--- 

и последний не является:

---end of record--- 

Обратите внимание, что не существует сопоставление среднего шаблона, что «,» является просто разделителем для обоих регулярных выражений.

+0

Мудрый человек однажды сказал, что любовь - это все, что вам нужно. В этом случае, однако, я думаю, вы в основном копировали строку 'awk', которую я имел в моем вопросе, не обращая внимания на остальные. – rojo

1

Вот мои усилия:

@ECHO OFF 
SETLOCAL 
:: 
:: Target username 
:: 
SET target=%1 
CALL :zaplines 
SET count=0 
FOR /f "delims=" %%I IN (rojoslog.txt) DO (
    ECHO.%%I| findstr /r "^20[0-9][0-9]-[0-9][0-9]-[0-9][0-9].[0-9][0-9]:[0-9][0-9]:[0-9][0-9]" >NUL 
    IF NOT ERRORLEVEL 1 (
    IF DEFINED founduser CALL :report 
    CALL :zaplines 
) 
    (SET stored=) 
    FOR /l %%L IN (1000,1,1200) DO IF NOT DEFINED stored IF NOT DEFINED line%%L (
    SET line%%L=%%I 
    SET stored=Y 
    ) 
    ECHO.%%I|FINDSTR /b /e /i /c:"account name: %target%" >NUL 
    IF NOT ERRORLEVEL 1 (SET founduser=Y) 
) 
IF DEFINED founduser CALL :report 
GOTO :eof 

:: 
:: remove all envvars starting 'line' 
:: Set 'not found user' at same time 
:: 
:zaplines 
(SET founduser=) 
FOR /f "delims==" %%L IN ('set line 2^>nul') DO (SET %%L=) 
GOTO :eof 

:report 
IF NOT DEFINED line1000 GOTO :EOF 
SET /a count+=1 
ECHO. 
ECHO.---------- START of record %count% ---------- 
FOR /l %%L IN (1000,1,1200) DO IF DEFINED line%%L CALL ECHO.%%line%%L%% 
ECHO.----------- END of record %count% ----------- 
GOTO :eof 
+0

Спасибо, Питер, и это хорошее усилие. К сожалению, для меня это слишком медленно. Я начал сценарий около 20 минут назад. С тех пор оба ядра моего процессора отскакивали от 70 до 100%, но скрипт все еще пытается пропустить первый файл журнала. Я не думаю, что смогу использовать чистое пакетное решение. – rojo

1

Ниже есть чистый Пакетное решение, которое не использует Grep. Он находит временные линии, потому что слово «резюме», которое не должно существовать в других строках, но это слово может быть изменено для другого, если это необходимо.

EDIT: Я изменил слово, которое идентифицирует линии метки времени «Auth.»; Я также изменил FINDSTR, чтобы игнорировать дело. Это новая версия:

@echo off 
setlocal EnableDelayedExpansion 

:parselog <username> <logfile> 
echo Searching %~2 for records containing %~1... 

set n=0 
set previousMatch=Auth. 
for /F "tokens=1* delims=:" %%a in ('findstr /I /N "Auth\. %~1" %2') do (
    set currentMatch=%%b 
    if "!previousMatch:Auth.=!" neq "!previousMatch!" (
     if "!currentMatch:Auth.=!" equ "!currentMatch!" (
     set /A n+=1 
     set /A skip[!n!]=!previousLine!-1 
    ) 
    ) else (
     set /A end[!n!]=%%a-1 
    ) 
    set previousLine=%%a 
    set previousMatch=%%b 
) 
if %n% equ 0 (
    echo No records found 
    goto :EOF 
) 

if not defined end[%n%] set end[%n%]=-1 
set i=1 
:nextRecord 
    echo/ 
    echo ---------------start of record %i%------------- 
    if !skip[%i%]! equ 0 (
     set skip= 
    ) else (
     set skip=skip=!skip[%i%]! 
    ) 
    set end=!end[%i%]! 
    for /F "%skip% tokens=1* delims=:" %%a in ('findstr /N "^" %2') do (
     echo(%%b 
     if %%a equ %end% goto endOfRecord 
    ) 
    :endOfRecord 
    echo ---------------end of record %i%------------- 
    set /A i+=1 
if %i% leq %n% goto nextRecord 

Пример команды:

C:>test user6q catch-all.log 

Результат:

Searching catch-all.log for records containing user6q... 

---------------start of record 1------------- 
2013-03-25 08:02:32 Auth.Critical 169.254.8.110 Mar 25 08:02:32 dc3 MSWinEventLog 2 Security 11730159 Mon Mar 25 08:02:29 2013 680 Security NT AUTHORITY\SYSTEM N/A Audit Failure dc3 9 Logon attempt by: MICROSOFT_AUTHENTICATION_PACKAGE_V1_0 

Logon account: USER6Q 

Source Workstation: dc3 

Error Code: 0xC0000234 
---------------end of record 1------------- 

---------------start of record 2------------- 
2013-03-25 08:02:32 Auth.Critical 169.254.8.110 Mar 25 08:02:32 dc3 MSWinEventLog 2 Security 11730160 Mon Mar 25 08:02:29 2013 539 Security NT AUTHORITY\SYSTEM N/A Audit Failure dc3 2 Logon Failure: 

    Reason:  Account locked out 

    User Name: [email protected] 

    Domain: MYDOMAIN 

    Logon Type: 3 

    Logon Process: Advapi 

    Authentication Package: Negotiate 

    Workstation Name: dc3 

    Caller User Name: dc3$ 

    Caller Domain: MYDOMAIN 

    Caller Logon ID: (0x0,0x3E7) 

    Caller Process ID: 400 

    Transited Services: - 

    Source Network Address: 169.254.7.89 

    Source Port: 55314 
---------------end of record 2------------- 

Этот метод использования только один выполнение findstr команды, чтобы найти все соответствующие записи , а затем еще одну команду findstr, чтобы показать каждую запись. Обратите внимание: первая команда for /F ... работает над результатами findstr "Auth. user..", а вторая команда for /F имеет опцию «skip = N» и GOTO, которые прерывают цикл, как только будет отображаться запись. Это означает, что команды FOR не замедляют работу программы; скорость этой программы зависит от скорости команды FINDSTR.

Однако возможно, что вторая команда for /F "%skip% ... in ('findstr /N "^" %2') занимает слишком много времени, так как размер результата вывода FINDSTR до его обработки FOR. Если это произойдет, мы можем изменить второй FOR другим быстрым способом (например, асинхронный канал, который будет разбит). Пожалуйста, сообщите результат.

Антонио

+0

+1, очень хорошая работа, очень быстрая с пустыми строками из записей, но без восклицательных знаков на выходе (DelayedExpansion). Мне нравится трюк «пропустить». – Endoro

+0

Я очень ценю работу, которую вы вкладываете в это. В сводке фактически не содержится слова «summary» (что не так уж важно, поскольку я могу 'findstr/n« Auth. »); но строка timestamp/summary не будет включать имя учетной записи, которое является скорее крупной сделкой. – rojo

+0

Я склоняюсь к использованию двоичного кода, такого как 'awk' или' grep' или похожего на синтаксическое разборку файлов, так как, по моему опыту, пакетные 'for' петли значительно медленнее. Сначала я также попробовал JScript 'textfile.ReadAll()'; но это заняло целую вечность и день на моих более 100 мегабайтах, 2,5 миллиона строк в час файлов журнала. Но мне любопытно узнать, эффективен ли ваш метод, если он исправлен, эффективнее, чем я пытался в прошлом. – rojo