LINUX.ORG.RU

Сравнение текстовых файлов большого объема

 


0

3

Доброго времени суток!

Существует следующая задача: имеется выгрузка журнала безопасности Windows в текстовом формате и есть еще один такой же файл содержащий другие события. Требуется сравнивать первый файл со вторым, находить уникальные записи событий и записывать их во второй файл.

Пример записи события журнала: https://drive.google.com/open?id=1PdLBdwunTAN9E3RbPKtO_uX6kdZeMHCt

Пробовал парсер на основе grep - он пропускает одинаковые поля событий, что есть главная загвоздка реализации.

Буду рад вашим идеям.

Ты с принтера его распечатай. Он под это видимо и заточен.
Распарсить, засунуть всё в базу данных, а там как-нибудь через sql.

crutch_master ★★★★★
()

Вот однострочник, должен сработать. Но лучше доработать.

diff 1 2 | awk '/>/ {print}' | sed 's/> //g' > out.txt

Deleted
()
Ответ на: комментарий от Deleted

Ты журнал то смотрел? Там блочный способ записи, а значит построчное сравнение в лоб не выйдет. Сначала нужно переформатировать журнал в удобный для сравнения вид, а потом уже производить манипуляции.

anonymous
()
Ответ на: комментарий от Deleted

И как по-твоему diff поймет, где начинается логический блок для сравнения, а где заканчивается, если в каждом таком блоке кол-во строк разное?

anonymous
()

Я бы сделал конечный автомат на awk.

legolegs ★★★★★
()
Ответ на: комментарий от anonymous

где начинается логический блок для сравнения

ТС этого не просил.

Требуется сравнивать первый файл со вторым, находить уникальные записи событий и записывать их во второй файл

Deleted
()
Ответ на: комментарий от Deleted

Как будто тут ТСы часто корректно вопросы формулируют. Ты на суть посмотри. Зачем ему построчное сравнение это? Оно ни о чем не говорит. Ему нужно сравнивать именно логические блоки-записи в журнале. Этого простым диффом не достичь.

anonymous
()
Ответ на: комментарий от Deleted

vimdiff имеет смысл при одинаковом количестве логических элементов и при сравнении 1 к 1. Ему невдомек, что ты хочешь кроме сравнения 1 к 1 (N-ный элемент первого файла с N-ным элементом второго файла) сравнивать еще и 1 ко всем (N-ный элемент первого файла со всеми элементами второго файла).

anonymous
()

КМК, нужно 1) как писал выше, переформатировать журнал в удобный для построчного сравнения вид (или полный логический блок в одну строку, или только нужную информацию из логического блока в одну строку), 2) сравнивать любой тулзой для построчного сравнения.

anonymous
()

Что такое «уникальные записи событий»? Должны быть одинаковы буква-в-букву? Или достаточно сравнивать по номерам и датам?

anonymous
()

Вот эта штука свернёт твой лог в последовательность закодированных в base64 строк, одна строка - одно событие. Можно их сравнивать как хочешь любыми средствами, diff, comm, sort, uniq.

< Security.log awk '/^[[:digit:]]/ { close("base64 -w0"); printf("\n"); } { print | "base64 -w0" }' > seclog.b64

Раскодируется банальным base64 -d < seclog.b64 > seclog.txt

Можно легко добавить в выхлоп авка id события, чтобы исходная сортировка не сбивалась.

anonymous
()
Ответ на: комментарий от anonymous

Тоже вариант. Наверное, самый оптимальный.

anonymous
()
Ответ на: комментарий от anonymous

При сравнении в файлах в каждом из них будут одинаковые (буква в букву) события. И чтобы их повторно не добавлять в конечный файл, их надо отфильтровывать

RihterRC
() автор топика
Ответ на: комментарий от RihterRC

Ну тут всё просто.

Тестовые данные:

$ cat log1.txt
3 jjjjj
 jjjjjj
2 aaaa
 hhhhhh
1 aaaa
 ggggg
$ cat log2.txt
4 ppppp
 qqqqqq
1 aaaa
 ggggg
# преобразуем в простую таблицу
 for i in log1.txt log2.txt; do <$i awk '/^[[:digit:]]/ { close("base64 -w0"); printf("\n%s\t",$1); } { print | "base64 -w0" }' | tail -n+2 > ${i%.*}.b64; done
# делаем слияние двух логов, отрезаем столбец с номерами и раскодируем:
sort -nr log?.b64 | uniq | cut -f2 |  base64.exe -d > full_log.txt
Результат:
$ cat full_log.txt
4 ppppp
 qqqqqq
3 jjjjj
 jjjjjj
2 aaaa
 hhhhhh
1 aaaa
 ggggg

anonymous
()
Ответ на: комментарий от anonymous

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

P.S. Во второй строчке кода .exe лишняя

RihterRC
() автор топика
Ответ на: комментарий от RihterRC

УМВР. Но во втором (с тремя событиями) файле у вас потерялась циферка, поэтому номер события стал 2731 и сортируется в конец итогового файла, может поэтому вы не увидели это событие?

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

Перепроверил все еще раз. Все равно не выходит ожидаемого результата.

Вот что получается у меня после выполнения кода: https://pastebin.com/Rkn0b2iW

Как будто он просто игнорирует uniq.

Ваш код я затолкал в скрипт: https://pastebin.com/gv8hWmQs

RihterRC
() автор топика
Ответ на: комментарий от RihterRC

Возможно, у вас несовпадение концов строк. Примените dos2unix к исходным файлам (или используйте такую функцию в текстовом редакторе). У меня событие 12730 успешно склеивается.

С событием 12729 ещё смешнее: у вас разное число пробелов после «Exit Status: 0x0», поэтому бинарно события разные. Можно откусить висячие победы седом или просто копипастить аккуратнее.

legolegs ★★★★★
()
Последнее исправление: legolegs (всего исправлений: 1)
Ответ на: комментарий от legolegs

Да, проверил, все работает как должно работать. Буду внимательней в следующий раз, спасибо :)

RihterRC
() автор топика
Ответ на: комментарий от legolegs

Возник еще один вопрос: как можно организовать сортировку по дате и времени при слиянии этих двух логов? Вопрос возник ввиду того, что система в винде, генерирующая данный журнал может присваивать одинаковый номер события (первый столбец), но дата и время события будут другие.

Первый лог: https://pastebin.com/T3wRRxXS

Второй лог с событием, у которого повторяется номер (событие 13345), но имеются другие дата/время: https://pastebin.com/gSWvR0PT

RihterRC
() автор топика
Ответ на: комментарий от RihterRC

Там где принтф печатать (вторым столбцом) ещё и дату, там где cut отрезать до 3 го столбца вместо 2го.

Возможно придётся встроить ещё один sort отдельно по столбцу с датами. Правда, даты сорт сортирует лексикографически.

legolegs ★★★★★
()
Последнее исправление: legolegs (всего исправлений: 1)
Ответ на: комментарий от legolegs

Правильно ли я понимаю то, что Вы предлагаете печатать третьим столбцом дату командой date как на примере ниже? Или Вы предлагаете дергать саму дату из событий?

for i in log1.log log2.log
do <$i awk -v date="$(date +"%B %d")" '/^[[:digit:]]/ { close("base64 -w0"); printf("\n%s\t",$1,date); } { print | "base64 -w0" }' | tail -n+2 > ${i%.*}.b64 
done 
RihterRC
() автор топика
Ответ на: комментарий от RihterRC

Правильно понимаете, да неправильно пишете, у вас и дата сегодняшняя и не печатается она.

Должно быть как-то так, но неправильно сортируются месяцы, завтра разберусь.

for i in log1.txt log2.txt; do
    <$i awk '
        /^[[:digit:]]/ {
            close("base64 -w0");
            printf("\n%s\t%s %s %s\t",$1,$2,$3,$4); /*id, month, day, time*/
        }
        {
            print | "base64 -w0"
        }
    ' | tail -n+2 > ${i%.*}.b64;
done;sort -k2 -Mr log?.b64 | sort -sk1 -nr | uniq | cut -f3 |  base64 -d > full_log.txt
legolegs ★★★★★
()
Ответ на: комментарий от legolegs

С сортировкой разобрался. Вроде работает как надо - сортировка идет по месяцу, дню месяца и времени.

Как вы думаете, такая реализация имеет место быть?

for i in log1.log log2.log
do <$i awk '/^[[:digit:]]/ { close("base64 -w0"); printf("\n%s\t%s %s %s\t",$1,$2,$3,$4); /*id, month, day, time*/ } { print | "base64 -w0" }' | tail -n+2 > ${i%.*}.b64 
done
sort -sk1nr log?.b64 | sort -k2Mr -k3nr -k4r | uniq | cut -f3 |  base64 -d > full_log.log 

RihterRC
() автор топика
Ответ на: комментарий от legolegs

Да, точно. Но и на номер события тоже полагаться нельзя - при одной сессии выгрузки журнала последнее событие может иметь, например, номер 12340 и дату Nov 10. А при следующей сессии выгрузки у последнего выгружаемого события номер может быть, например, 12243, а дата Nov 13. И насколько я понимаю, выгрузка формирует дату в таком виде только лишь потому что хранит события не более года.

RihterRC
() автор топика
Ответ на: комментарий от RihterRC

Вроде разобрался. Были непонятки с sort из-за локали и из-за того, что в мане плохо описан ключ -k.

for i in log1.txt log2.txt; do
    <$i awk '
        /^[[:digit:]]/ {
            close("base64 -w0");
            printf("\n%s\t%s\t%s %s\t",$1,$2,$3,$4); /*id, month, day, time => id, month, day+time*/
        }
        {
            print | "base64 -w0"
        }
    ' | tail -n+2 > ${i%.*}.b64;
done
LANG=C sort -t$'\t' -k1nr -k2Mr -k3r -u log?.b64 | cut -f4 | base64 -d > full_log.txt

Если, допустим, тестовые данные такие, что:

# cut -f1,2,3 log?.b64
12730   Nov     08 07:30
12729   Nov     08 07:30
12729   Jan     20 12:34
12729   Feb     20 12:34
12729   Jun     20 12:34
12729   Dec     20 12:34
12729   Nov     08 07:30
12731   Nov     08 07:31
12730   Nov     08 07:30
12729   Nov     08 07:30

то, меняя порядок -k...-ключей у sort можно выбирать, по каким столбцам в каком порядке сортировать. Например:

# LANG=C sort -t$'\t' -k1nr -k2Mr -k3r -u log?.b64| cut -f1,2,3
12731   Nov     08 07:31
12730   Nov     08 07:30
12729   Dec     20 12:34
12729   Nov     08 07:30
12729   Nov     08 07:30
12729   Jun     20 12:34
12729   Feb     20 12:34
12729   Jan     20 12:34

Или, если номера не обязательно последовательны, то сначала опираемся на дату:

# LANG=C sort -t$'\t' -k2Mr -k3r -k1nr -u log?.b64| cut -f1,2,3
12729   Dec     20 12:34
12731   Nov     08 07:31
12730   Nov     08 07:30
12729   Nov     08 07:30
12729   Nov     08 07:30
12729   Jun     20 12:34
12729   Feb     20 12:34
12729   Jan     20 12:34

Ключи sort: число - номер поля, M - сортировка месяцев JAN<FEB<...<DEC , n - числовая сортировка, r - задом наперёд.

legolegs ★★★★★
()
Последнее исправление: legolegs (всего исправлений: 2)
Ответ на: комментарий от legolegs

Да, эта вариация работает как надо, спасибо!

P.S.

LANG=C sort -t$'\t' -k1nr -k2Mr -k3r -u log?.b64 | cut -f4 | base64 -d > full_log.txt 
Здесь должно быть не f4, а f3 :)

RihterRC
() автор топика
Ответ на: комментарий от RihterRC

Здесь должно быть не f4, а f3 :)

Ээээ. Нет. base64 с полным текстом записи журнала хранится в 4м поле, его и извлекаем. А первые 3 после сортировки по ним выкидываем.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

Я применил Ваш конечный код на своих данных, и в итоге получал пустой файл на выходе, пока не поменял 4 на 3. При смене появились склеенные данные с правильной сортировкой

RihterRC
() автор топика
Ответ на: комментарий от RihterRC

Вот я не уверен, что сортировка будет правильной при таком раскладе. cut -f1,2,3 log?.b64 должно давать выхлоп как у меня в примере выше, а cut -f4 log?.b64 - чистый base64.

legolegs ★★★★★
()
22 января 2019 г.
Ответ на: комментарий от legolegs

И все таки этот код работает прекрасно только с cut -f3 log?.b64. Плюс добавил ко всему этому сортировку событий по годам (раскидывая журналы в папки соответствующие году событий) и все работает, шуршит :)

RihterRC
() автор топика
Ответ на: комментарий от RihterRC

Ну хорошо, что работает, хотя я и не понимаю как.

Надеюсь, через год-другой при расследовании какого-либо инцидента вы не обнаружите, что с моей помощью собрали невалидный архив :)

legolegs ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.