Zwiększenie prędkości skryptu PS, zliczanie adresów IP z pliku tekstowego

0

Stworzyłem skrypt, który pomaga mi analizować pliki dziennika dostępu bez SSH na serwerze (mam tylko ten plik). Liczy i sortuje liczbę adresów IP, którymi zarządzam, ale okazało się, że w przypadku dużych plików zajmuje to zbyt dużo czasu (jest to BARDZO podstawowe). Nie chciałem używać skompilowanej aplikacji i nie mam SSH na serwerze, więc zwróciłem się do Powershell.

$sw = [Diagnostics.Stopwatch]::StartNew()
$input_path = ‘c:\temp\access_log2’
$ip_file = ‘c:\temp\IPs.txt’
$output_file = ‘c:\temp\SORTED.txt’
$regex = '\b(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\b'
select-string -Path $input_path -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value } > $ip_file
get-content $ip_file | group-object -noelement | Sort-Object count -descending > $output_file
get-Content $output_file -First 25
$sw.Stop()
$sw.Elapsed

Też próbowałem

$regex = ‘\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b’

na pliku 5 MB (14,4 tys. linii) zajęło 18 minut na pliku 37 MB (158,5 tys. linii), zajęło to ponad 3 godziny

Stoper służy tylko do moich testów. Skrypt służy tylko do pobrania adresu IP, policzenia ich i posortowania według najczęściej występujących. Być może zapisywanie plików jest największym spowolnieniem, ale nie znam zbytnio zmiennych przechowywanych w pamięci RAM. Wydaje mi się, że istnieje lepszy sposób na wyodrębnienie adresów IP (może po prostu użyć pierwszych 15 znaków w wierszu?). Oto przykład linii, Combined Log Format

21.198.52.3 - - [06/Aug/2017:11:31:54 -0400] "GET / HTTP/1.0" 301 452 "-" "-"
154.212.178.24 - - [06/Aug/2017:11:10:44 -0400] "GET /images/12345.jpg HTTP/1.1" 200 212443 "-" "Mozilla/5.0 (compatible; AhrefsBot/5.2; +http://ahrefs.com/robot/)"

Jakakolwiek rada?

loserspearl
źródło
jeśli wiersze dziennika zawsze zaczynają się od prawidłowego adresu IP, po co używać złożonego RegEx do sprawdzania ważności?
LotPings,
Robią, czy mogę korzystać z innych funkcji .net, takich jak IPAddress.TryParse, jak w przypadku sprawdzania poprawności adresów IP dla c #?
loserspearl

Odpowiedzi:

0

Jest prostsze (miejmy nadzieję szybsze podejście), jeśli ip prowadzi każdą linię. Prosty podział w przestrzeni powinien oddzielić ip.

Podczas przetwarzania linii możesz zwiększyć tablicę hasht, zaadresowaną przez ip

$sw = [Diagnostics.Stopwatch]::StartNew()
$input_path  = 'c:\Temp\access_log2'

$Hash = @{} 
ForEach ($Line in (Get-Content $Input_path)) {
    ++$Hash[$Line.split(' ')[0]]
} 
$Hash.GetEnumerator() | sort -Property Value -desc |Select -First 25
$sw.Stop()
$sw.Elapsed

Przykładowe dane wyjściowe tylko z replikowanymi liniami:

Name                           Value
----                           -----
154.212.178.24                 5
21.198.52.3                    4

Ticks             : 121118
Days              : 0
Hours             : 0
Milliseconds      : 12
Minutes           : 0
Seconds           : 0
TotalDays         : 1,4018287037037E-07
TotalHours        : 3,36438888888889E-06
TotalMilliseconds : 12,1118
TotalMinutes      : 0,000201863333333333
TotalSeconds      : 0,0121118
LotPings
źródło
Count Name ----- ---- ...52 127.171.123.44 Name Value ---- ----- 127.171.123.44 145214To nie odcina wartości zliczania, gdy ma ponad 6 cyfr, ani nie przechwytuje adresów IP agentów, które mogą znajdować się poniżej wiersza zapisu dziennika, noice
loserspearl
0

Dlaczego zapisujesz swoje dane w pliku pośrednim ( $ip_file)? Dlaczego nie przekazać go bezpośrednio do obiektu grupy? Być może coś takiego?

$sw = [Diagnostics.Stopwatch]::StartNew()

$input_path  = ‘c:\temp\access_log2’
$ip_file     = ‘c:\temp\IPs.txt’
$output_file = ‘c:\temp\SORTED.txt’
$regex       = '\b(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\b'

Select-String -Path $input_path -Pattern $regex -AllMatches |
  ForEach-Object { $_.Matches } |
  ForEach-Object { $_.Value } |
  Group-Object -noelement |
  Sort-Object count -descending > $output_file
Get-Content $output_file -First 25

$sw.Stop()
$sw.Elapsed

Jeśli to nie przyspieszy, prawdopodobnie powinieneś spróbować dowiedzieć się, która część skryptu jest wolna.

Może zacznij od zrobienia czegoś takiego. Następnie dodaj trochę funkcjonalności, aby zobaczyć, gdzie jest powolność.

$sw = [Diagnostics.Stopwatch]::StartNew()
$input_path  = ‘c:\temp\access_log2’
$regex       = '\b(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\b'
Select-String -Path $input_path -Pattern $regex -AllMatches | Out-Null
$sw.Stop()
$sw.Elapsed

Jeśli chcesz założyć, że plik dziennika jest zawsze poprawnie sformatowany, prawdopodobnie możesz przyspieszyć i po prostu pobrać pierwszą kolumnę. (To przetworzyło 233 MB, 1 000 749 linii pliku dziennika apache na moim komputerze w około 15 sekund)

Get-Content c:\temp\access_log2 | ForEach-Object { $_.split(' ')[0] } |
Group-Object -NoElement |
Sort-Object Count
Zoredache
źródło
Nie znam się zbyt dobrze na PS, więc zacząłem od uruchomienia. Nowe orurowanie działa znacznie lepiej. Zmieniłem też na szybszy HDD, co sprowadza go do kilku minut. TY
loserspearl