^
и $
не имеют значений (начало или конец строки )внутри [ ]
, которые используются для выбора группы символов. Внутри [ ]
большинство операторов регулярных выражений теряют свое значение (, а некоторые символы приобретают особое значение или приобретают другое значение ). Ведущий ^
в []
означает, что группа инвертирована -соответствует всем , кроме этих символов. Таким образом, [^$]
соответствует любому символу, кроме $
. (И [$^]
соответствуют только символам $
и ^
.)
Если вы хотите сопоставить начало или конец строки, используйте /^\|$/
, где |
равно или(необходимо экранировать в режиме регулярных выражений Vim по умолчанию ).
Так:
:%s/^\|$/'/g
g
необходим, поскольку ^
и $
являются двумя независимыми совпадениями, а s
по умолчанию действует только на первое совпадение в строке.
icarus уже ответил на этот вопрос в awk
, так что вот как извлечь дату и идентификатор в переменные, а данные события в хэш (ассоциативный массив )с помощьюperl
:
#!/usr/bin/perl -l
use strict;
while(<>) {
if (m/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}).+?\sID\s(\[\d{4}\]).*?Data -> (.*)$/) {
my ($date,$id,$eventdata) = ($1,$2,$3);
print $date;
print $id;
# decorate the key names with a tab (i.e. add a tab before each)
$eventdata =~ s/([^[:blank:]]+) *= */\t$1=/g;
# remove tab from beginning of $eventdata
$eventdata =~ s/^\t//; #/
# split $eventdata on tabs, and split again into key=value pairs
# and store in %data hash.
my %data = map { my($k,$v) = split("=",$_,2); $k => $v } split(/ *\t/,$eventdata);
foreach my $key (sort keys %data) { printf "%s=%s\n", $key, $data{$key} };
};
};
(Комментарий #/
предназначен только для исправления неработающей подсветки синтаксиса Perl U&L)
Обратите внимание, что ,2
в конце операции split("=",$_,2)
каждая пара ключ=значение разбивается максимум на два поля :все до первого=
символ и все, что после него. Это означает, что не имеет значения, содержит ли значение символ =
. Такие вещи гораздо проще сделать в perl, чем в awk. Работа с регулярными выражениями и группами захвата также упрощается, как показано в первых двух строках в начале цикла while(<>)
.
Сохранить как, например. kei.pl
, сделайте его исполняемым с помощью chmod +x kei.pl
и запустите так:
$./kei.pl input
2017-03-21T02:00:00
[4624]
AuthenticationPackageName=Negotiate
ImpersonationLevel=%%1122
IpAddress=10.0.0.0
IpPort=10.5.3.2
KeyLength=0
LmPackageName=Stainless
LogonGuid={00344000-0000-0000-0000-0000000003440}
LogonProcessName=Lxxoi
LogonType=8
ProcessId=0x0000000000000244
ProcessName=C:/Windows/System32/services.exe
SubjectDomainName=WORKGROUP
SubjectLogonId=0x00000000000004j7
SubjectUserName=PRETENDERS$
SubjectUserSid=S-1-5-18
TargetDomainName=NT AUTHORITY
TargetLogonId=0x00000000000003e7
TargetUserName=SYSTEMS
TargetUserSid=X-12-54-181
TransmittedServices=-
WorkstationName=-
Кстати, если вы хотите, чтобы дата и идентификатор также были в хеше, добавьте следующее после строки %data = map...
(и удалите строки print $date;
и print $id;
:
$data{'DATE'} = $date;
$data{'ID'} = $id;
Проблема с журналами безопасности заключается в том, что часть текста, вероятно, находится под контролем пользователя, поэтому использование регулярных выражений для разделения данных проблематично. Однако вы потенциально можете использовать более одного выражения, чтобы разделить вещи, и это может обойти ограничение в 9 обратных ссылок. Например, если все ваши записи в журнале начинаются с временной метки, вы можете удалить ее.
awk '{t=$1 ;$1="";
print gensub(/^.+?\sID\s(\[[0-9]{4}\]).+?\sTargetUserName\s=\s(.+?)\sTargetDomainName\s=\s(.+?)\sTargetLogonId\s=\s(.+?)\sLogonType\s=\s([0-9]{1,2})\s(.+?\sWorkstationName\s=\s(.+?)\sLogonGuid\s=\s.+?TransmittedServices\s=\s.+?\sLmPackageName\s=\s.+?KeyLength\s=\s.+?\sProcessId\s=\s.+?\sProcessName\s=\s.+?\sIpAddress\s=\s(.+?)\sIpPort\s\=\s([0-9]{1,}))?.+?$/,"\\4,\\3,\\2,\\1\\5" t ",\\7,\\8,","g") }'
Вы можете быть избирательным, поэтому у вас есть WorkstationName\s=\s(.+?)\sLogonGuid
как часть вашего шаблона, вы можете использовать
awk {t=$1; $1="" ; printf("%s", gensub(/^.+?WorkstationName\s=\s(.+?)\sLogonGuid.*$/,"\\1,")); printf("%s,", t)}
вытащить поле, и это можно повторить.
@cas отмечает в комментариях, что данные можно просматривать в 2 частях, материал до EventData/Data ->
и материал после него, и что материал после него можно разделить на=
(пространство равное пространство ). Я бы пошел дальше и рассмотрел его как пары ключ/значение и разделил на /\s\S+\s=\s/
и использовал необязательный 4-й аргумент для split
, чтобы получить ключи. В этом есть пара больших предположений, что пользователь не может поместить знак равенства в строку и что каждый фрагмент данных имеет ключ из одного слова. Обратите внимание, что индексы ключей и значений отличаются на 1, и что начальная часть строки заканчивается на v[1]
.
/usr/bin/awk '{
n=split($0,v,/\s\S+\s=\s/,k)
printf("There are %d fields\n",n)
for(i=0;i<n;i++) { printf("%d key \"%s\" value \"%s\"\n",i,k[i],v[i+1]) }
}'
с вашими примерными данными дает
There are 22 fields
0 key "" value "2017-03-21T02:00:00 kornawesome Security/Microsoft-Windows-Security-Auditing ID [4624] :EventData/Data ->"
1 key " SubjectUserSid = " value "S-1-5-18"
2 key " SubjectUserName = " value "PRETENDERS$"
3 key " SubjectDomainName = " value "WORKGROUP"
4 key " SubjectLogonId = " value "0x00000000000004j7"
5 key " TargetUserSid = " value "X-12-54-181"
6 key " TargetUserName = " value "SYSTEMS"
7 key " TargetDomainName = " value "NT AUTHORITY"
8 key " TargetLogonId = " value "0x00000000000003e7"
9 key " LogonType = " value "8"
10 key " LogonProcessName = " value "Lxxoi "
11 key " AuthenticationPackageName = " value "Negotiate"
12 key " WorkstationName = " value "-"
13 key " LogonGuid = " value "{00344000-0000-0000-0000-0000000003440}"
14 key " TransmittedServices = " value "-"
15 key " LmPackageName = " value "Stainless"
16 key " KeyLength = " value "0"
17 key " ProcessId = " value "0x0000000000000244"
18 key " ProcessName = " value "C:/Windows/System32/services.exe"
19 key " IpAddress = " value "10.0.0.0"
20 key " IpPort = " value "10.5.3.2"
21 key " ImpersonationLevel = " value "%%1122"
Отсюда вы можете пойти дальше, создать ассоциативный массив с именем say data
for(i=1;i<n;i++) {gsub(/[ =]/,"",k[i]);data[k[i]]=v[i+1]}
, а затем вы можете распечатать что-то вроде data["IpPort"]
, не беспокоясь о том, поле это 20 или 21.