로그 파일에서 봇 활동을 필터링하는 작업을 완료해야 합니다.
솔루션은 다음 기준을 충족하는 레코드만 표시해야 합니다.
- 사용자는 로그인하고, 비밀번호를 변경하고, 동일한 순간에 로그아웃합니다.
- 이러한 작업(로그인, 비밀번호 변경, 로그아웃)은 사이에 다른 항목 없이 차례로 발생합니다.
입력 데이터 예시
[a lot of data]
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged in| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user changed password| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged off| -
Mon, 22 Aug 2016 13:15:42 +0200|178.57.66.225|faaaaaa11111| - |user logged in| -
Mon, 22 Aug 2016 13:15:40 +0200|178.57.66.215|terdsfsdfsdf| - |user logged in| -
Mon, 22 Aug 2016 13:15:49 +0200|178.57.66.215|terdsfsdfsdf| - |user changed password| -
Mon, 22 Aug 2016 13:15:49 +0200|178.57.66.215|terdsfsdfsdf| - |user logged off| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged in| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user changed password| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged off| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user logged in| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user changed password| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user changed profile| -
Mon, 22 Aug 2016 13:17:50 +0200|178.57.66.205|abcbbabab| - |user logged off| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged in| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user changed password| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged off| -
Mon, 22 Aug 2016 13:20:42 +0200|178.57.67.225|faaaa0a11111| - |user logged in| -
[a lot of data]
작업을 수행하기 위해 다음 코드를 작성했습니다.
awk 'BEGIN { FS=" " } { c[$5]++; l[$5,c[$5]]=$0 } END { for (i in c) { if (c[i] == 3) for (j = 1 ; j <= c[i]; j++) print l[i,j] } }' $1
용법:
./parse_log.sh 로그 파일.log
산출:
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged in| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user changed password| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged off| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged in| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user changed password| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged off| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged in| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user changed password| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged off| -
하지만 Perl이나 Python으로 작성된 대안(외부 라이브러리를 최소한으로 사용)이 어떤 모습일지 궁금합니다.
답변1
이것은 답변은 아니지만 주석으로 쓰기에는 너무 커서 형식을 지정해야 하므로 "Python 코드가 수행하는 작업을 읽고 이해하기가 더 쉽습니다."라는 주석을 처리하려면 합리적인 변수 이름을 사용하는 AWK 스크립트인 this 내가 한 일이야생각하다 당신의 파이썬 스크립트Python 스크립트와 많이 비슷해 보이지만 awk는 텍스트를 조작하기 위해 Python에서 코딩해야 하는 모든 일반적인 작업을 이미 수행하므로 더 짧습니다.
awk -v column=5 '
{ records[$column] = records[$column] $0 ORS }
END {
for ( timestamp in records ) {
if ( gsub(ORS,"&",records[timestamp]) > 2 ) {
printf "%s", records[timestamp]
}
}
}
' logfile.log
그러나 처리하기 전에 전체 파일을 메모리로 읽는 것은 이 문제를 해결하는 매우 비효율적인 방법입니다. 시간이 바뀔 때마다 테스트하고 인쇄해야 합니다.
awk -v column=5 '
$column != prev {
prt()
records = ""
prev = $column
}
{ records = records $0 ORS }
END { prt() }
function prt() {
if ( gsub(ORS,"&",records) > 2 ) {
printf "%s", records
}
}
' logfile.log
답변2
솔루션 자체는 Python 3으로 작성되었습니다.
#!/usr/bin/env python3
import sys
import re
from collections import defaultdict
column_delimiter = sys.argv[1]
column = int(sys.argv[2]) - 1
records = defaultdict(list)
with open(sys.argv[3]) as inputfile:
for lines in inputfile:
line = lines.rstrip('\n')
row_record = line.split(column_delimiter)
records[row_record[column]].append(line)
for timestamps in records.values():
if len(timestamps) == 3:
for i in range(len(timestamps)):
if (re.search('logged in|changed password|logged off', timestamps[i])):
print(timestamps[i])
용법:parse_log.py ' ' 5 logfile.log
Python 코드는 읽고 이해하기가 더 쉽습니다.
답변3
무엇이든 awk
:
#!/usr/bin/awk -f
BEGIN { FS = "[|]" }
prvHour == $1 && prvUsr == $3 {
if ($(NF-1) == "user logged in" ||
$(NF-1) == "user changed password" ||
$(NF-1) == "user logged off" )
actions[++actionCnt] = $0
else actionCnt = 0
}
prvHour != $1 && prvUsr != $3 {
if (prvHour && actionCnt == 3)
for (i = 1; i <= actionCnt ; i++)
print actions[i]
prvHour = $1; prvUsr = $3
actionCnt = 0 ; actions[++actionCnt] = $0
}
END {
if (actionCnt == 3)
for (i = 1; i <= actionCnt; i++)
print actions[i]
}
Perl
외부 라이브러리를 사용 하지 않고 :
/bin/perl -e '
while (1) {
$uli = $uli // <>;
$ucp = <> if $uli =~ /^([^|]*)[|][^|]*[|]([^|]*)[|] - [|]user logged in[|] -$/;
last if tell() < 0 ;
if (!defined $ucp) { $uli = undef ; next; }
$ulo = <> if $ucp =~ /^(\Q$1\E)[|][^|]*[|]($2)[|] - [|]user changed password[|] -$/;
last if tell() < 0;
if (!defined $ulo) { $uli = $ucp ; $ucp = undef ; next ; }
if ($ulo !~ /^\Q$1\E[|][^|]*[|]$2[|] - [|]user logged off[|] -$/) {
$uli = $ulo ; $ucp = $ulo = undef ; next ;
}
print "$uli$ucp$ulo";
$uli = $ucp = $ulo = undef;
}
' sample
python3
usgin을 사용하면 sys
파일을 읽고 exit
의미 있는 값으로 호출할 수 있습니다.
#!/bin/python3
import sys
try:
fullLine = actions = []
prvHour = prvUsr = None
chk_act = lambda x: x == "user logged in" or \
x == "user changed password" or \
x == "user logged off"
with open(sys.argv[1]) as logFile:
for line in logFile:
hour, _, user, _, action, _ = line.split('|')
if prvHour == hour and prvUsr == user:
fullLine.append(line.strip())
actions.append(action.strip())
elif prvUsr != user and prvHour != hour:
if len(actions) == 3 and all(map(chk_act, actions)):
print("\n".join(fullLine))
prvUsr = user
prvHour = hour
actions = []
actions.append(action.strip())
fullLine = []
fullLine.append(line.strip())
except IndexError:
print("usage {} logfile".format(sys.argv[0]))
sys.exit(1)
except (FileNotFoundError, PermissionError):
print("{} not found or permission permission denied", sys.argv[1])
sys.exit(1)
무엇이든 sed
:
#!/bin/sed -nf
N;/^\([^|]*\)|[^|]*|\([^|]*\)| - |user logged in| -\n\1|[^|]*|\2| - |user changed password| -$/{
N;/\n\([^|]*\)|[^|]*|\([^|]*\)| - |user changed password| -\n\1|[^|]*|\2| - |user logged off| -$/{
p;b
}
s/.*\n/\n/g;D
}
D
모든 솔루션은 전체 데이터를 메모리에 저장하지 않습니다.
답변4
Perl에서는 한 줄씩 작성할 수 있지만 약간 혼란스럽습니다.
perl -MTime::Piece -F'\|' -ae '$epoch=Time::Piece->strptime($F[0], "%a, %d %b %Y %H:%M:%S %z")->epoch; $diff=$epochlast2 - $epoch; $last =~ /user changed password/ && $last2 =~ /user logged in/ && $_ =~ /user logged off/ && $diff==0 && print $last2, $last, $_; $epochlast2=$epochlast; $epochlast=$epoch; $last2=$last; $last=$_' <<< "$data"
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged in| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user changed password| -
Mon, 22 Aug 2016 13:15:39 +0200|178.57.66.225|fxsciaqulmlk| - |user logged off| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged in| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user changed password| -
Mon, 22 Aug 2016 13:15:59 +0200|178.57.66.205|erdsfsdfsdf| - |user logged off| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged in| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user changed password| -
Mon, 22 Aug 2016 13:19:19 +0200|178.56.66.225|fxsciaqulmla| - |user logged off| -
또는 스크립트로:
use warnings;
use Time::Piece;
my $epochlast2=0;
my $epochlast=0;
my $last2="";
my $last="";
while($line = <STDIN>){
$date=(split(/\|/, $line))[0];
$epoch=Time::Piece->strptime($date, "%a, %d %b %Y %H:%M:%S %z")->epoch;
$diff=$epochlast2 - $epoch;
if ($last =~ /user changed password/ && $last2 =~ /user logged in/ && $line =~ /user logged off/ && $diff==0) {
print $last2, $last, $line;
}
$epochlast2=$epochlast;
$epochlast=$epoch;
$last2=$last;
$last=$line
}