2017-01-24 11 views
1

Это мое первое сообщение о stackoverflow. \ 0/ Надеюсь, это не слишком длинная запись. Я пишу сценарий BASH для регулярного чтения, фильтрации и вывода данных из тысяч лог-файлов. Производительность важна, поэтому я использую grep вместо awk или sed.Bash Как эффективно манипулировать многострочным выходом grep -Poz?

grep -Poz делает именно то, что я хочу, для захвата (многострочных) данных с использованием шаблонов, которые важны для дальнейшей обработки, но я застрял в манипулировании данными, например, с XML-файлом или пакетом SQLite3, запрос для дальнейшего анализа.

#!/bin/bash 
# Regex: 
# (?s) multiline search 
# Capturegroup 1 = date 
# Capturegroup 2 = time 
# Capturegroup 3 = error type (ERROR, WARN or DEBUG) 
# Capturegroup 4 = error details 
# Positive lookahed, until new line (windows/linux) starts with date, OR (if it's the last line matching the pattern, till the end of the last line. 
# 
REGEX_MULTILINE="(?s)([0-9]{4}-[0-9]{2}-[0-9]{2})[[:space:]]([0-9]{2}:[0-9]{2}:[0-9]{2}[,|.][0-9]{3})[[:space:]]+(ERROR|WARN|DEBUG)(.*?)(?=(?:\r\n|[\r\n])[0-9]{4}-[0-9]{2}-[0-9]{2}|\z)" 
LOGFILE="test.log" 

# write to logfile gives exactly the info I want 
write_log(){ 
    echo -n $(grep -Pzo $REGEX_MULTILINE $LOGFILE) > output_grep1.txt 
} 

# I'm stuck in this part to generate, for example, an XML-file 
write_xml(){ 
    local LOGDATE="" 
    local LOGTIME="" 
    local LOGTYPE="" 
    local LOGINFO="" 
    while IFS= read -r LINE ; do 
    #For testing purposes, to see if brackets contain the full string, 
    #or a line of that string 
    printf '%s\n' "[$LINE]" 
    #processing logic here. Didn't get this far yet 
    while [[ $LINE =~ $REGEX_MULTILINE ]] ; do 
     # regex capturegoups 
     LOGDATE=${BASH_REMATCH[1]} 
     LOGTIME=${BASH_REMATCH[2]} 
     LOGTYPE=${BASH_REMATCH[3]} 
     LOGINFO=${BASH_REMATCH[4]} 
     # send vars to function for output 
     # write_xml_function $LOGDATE $LOGTIME $LOGTYPE $LOGINFO 
     # for testing purposes 
     echo -e "log entry:\n\t 1: $LOGDATE \n\t 2: $LOGTIME \n\t 3: $LOGTYPE \n\t 4: $LOGINFO \n" 
     break 
    done 
done < <(grep -Pzo $REGEX_MULTILINE $LOGFILE) 
} 

Файл протокола может выглядеть примерно так:

2017-01-01 11:09:42,439 INFO server.service.function.property.PropertyService - Props (re)loaded. 
2017-01-01 11:15:46,155 DEBUG server.service.ApiController - api/start called! params: 
${params} 
2017-01-01 13:01:29,675 ERROR server.service.util.base.FtpClient - Error retrieving file. Directory does not exist. 
2017-01-01 13:15:12,803 DEBUG server.service.ApiController - api/start called! params: 
${params} 
2017-01-01 13:15:13,932 INFO server.service.ControllerService - Filter:server.service.model.Filters 
2017-01-01 15:36:04,914 INFO server.service.ControllerService - Filter:server.service.model.Filters 
2017-01-01 15:55:50,279 ERROR server.service.WebClient - server API failed: [(someError.java:12345)] 
{"someId":"etc","otherId":123,"token":{}} 
2017-01-01 15:55:50,366 ERROR server.service.controller.Search - Server error for [/service/search/load]: java.lang.NullPointerException stack[etc] 
java.lang.NullPointerException 
    at server.common.stack(SomeApi.java:123) 
    at server.service.trace(SomeService.java:456) 
    at java.lang.Thread.run(Thread.java:789) 
    etc. 
    etc. 
2017-01-01 16:17:55,175 DEBUG server.config.app - 

STARTING... 


2017-01-01 16:18:00,040 INFO server.common.service.base.property - Props (re)loaded. 
2017-01-01 17:44:43,959 DEBUG server.service.controller - api/start called! params: 
${params} 

В результате я ожидаю, при чтении строки Grep многострочный это:

[2017-01-01 13:15:13,932 INFO server.service.ControllerService - Filter:server.service.model.Filters] 
[2017-01-01 15:36:04,914 INFO server.service.ControllerService - Filter:server.service.model.Filters] 
[2017-01-01 15:55:50,279 ERROR server.service.WebClient - server API failed: [(someError.java:12345)] 
{"someId":"etc","otherId":123,"token":{}}] 
[2017-01-01 15:55:50,366 ERROR server.service.controller.Search - Server error for [/service/search/load]: java.lang.NullPointerException stack[etc] 
java.lang.NullPointerException 
    at server.common.stack(SomeApi.java:123) 
    at server.service.trace(SomeService.java:456) 
    at java.lang.Thread.run(Thread.java:789) 
    etc. 
    etc.] 

Вместо этого я получаю это:

[2017-01-01 13:15:13,932 INFO server.service.ControllerService - Filter:server.service.model.Filters] 
[2017-01-01 15:36:04,914 INFO server.service.ControllerService - Filter:server.service.model.Filters] 
[2017-01-01 15:55:50,279 ERROR server.service.WebClient - server API failed: [(someError.java:12345)] 
{"someId":"etc","otherId":123,"token":{}}] 
[2017-01-01 15:55:50,366 ERROR server.service.controller.Search - Server error for [/service/search/load]: java.lang.NullPointerException stack[etc]] 
[java.lang.NullPointerException] 
[ at server.common.stack(SomeApi.java:123)] 
[ at server.service.trace(SomeService.java:456)] 
[ at java.lang.Thread.run(Thread.java:789)] 
[ etc.] 
[ etc.] 

Что я переделал смотреть? Это можно сделать так?

+0

'grep' работает линия за линией, а не целые файлы. Вам нужно будет использовать что-то еще, например Perl или Python. – Samadi

+3

@ Самади, с '-z', это не строка-лайн. –

+2

@Asgair, как имена переменных в стороне - все-шапки, заданы POSIX для использования в переменных, имеющих значение для операционной системы или оболочки, тогда как имена с хотя бы одним строчным символом зарезервированы для использования приложения; вы должны использовать имена в последнем классе. Если в будущей версии вашей оболочки добавится новый встроенный шаблон, это гарантирует, что вы ничего не используете, чтобы перезаписать его случайно. –

ответ

1

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

Вы должны быть в состоянии использовать

while IFS= read -r -d '' LINE ; do 
+0

* grumble * re: демонстрация имени переменной all-caps, в отличие от [POSIX руководящих принципов] (http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html) (обратите внимание, что поскольку установка регулярной переменной оболочки будет перезаписывать переменную среды с похожими именами, соглашения об именах для одного класса обязательно применимы к обоим). –

+0

Согласен, но я думаю, что лучше ответить на вопросы, используя имена переменных из предоставленного кода. –

+1

Есть аргумент, который нужно сделать обеими способами, определенно - но поскольку ответы StackOverflow предоставляют канонический код, который должен быть замечен и скопирован другими (а не только ОП задает вопрос!), Есть смысл продемонстрировать в нем хорошие практики. –