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?


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

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.