Aumentando a velocidade do script PS, contando IPs do arquivo de texto

0

Eu fiz um script para me ajudar a analisar os arquivos de log de acesso sem SSH para o servidor (eu tenho apenas o arquivo). Ele conta e classifica o número de sites de controle de endereços IP que eu gerencio, mas descobri que leva muito tempo em arquivos grandes (é MUITO rudimentar). Eu não queria ter que usar um aplicativo compilado e não tenho SSH para o servidor, então eu voltei para o 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

Eu também tentei

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

no arquivo de 5MB (14.4K linhas), demorou 18 minutos em um arquivo de 37MB (158,5K linhas), levou mais de 3 horas

O cronômetro é apenas para o meu teste. O script é apenas para puxar o IP, contá-los e classificar por mais ocorrendo. Talvez a gravação de arquivos seja a maior lentidão, mas eu não estou muito familiarizado com variáveis armazenadas na RAM. Eu acho que há uma maneira melhor de extrair endereços IP (talvez apenas com os primeiros 15 caracteres por linha?). Aqui está um exemplo das linhas, 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/)"

Algum conselho?

    
por loserspearl 10.08.2017 / 18:52

2 respostas

0

Existe uma abordagem mais simples (esperançosamente mais rápida) se o ip estiver liderando todas as linhas. Uma simples divisão no espaço deve separar o ip.

Ao processar as linhas, você pode incrementar uma hashtable endereçada pelo 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

Exemplo de saída com apenas suas linhas replicadas:

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
    
por 10.08.2017 / 22:45
0

Por que você está escrevendo seus dados em um arquivo intermediário ( $ip_file )? Por que não passá-lo diretamente para o Group-Object? Talvez algo assim?

$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

Se isso não tornar as coisas mais rápidas, provavelmente você deve tentar descobrir qual parte do seu script é a parte lenta.

Talvez comece apenas fazendo algo assim. Em seguida, adicione a funcionalidade de volta um pouco de cada vez para ver onde está a lentidão.

$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

Se você estiver disposto a assumir que o arquivo de log está sempre formatado corretamente, provavelmente poderá acelerar as coisas e apenas pegar a primeira coluna. (Isso processou um arquivo de log apache de 233MB, 1.000.749 linhas no meu computador em cerca de 15 segundos)

Get-Content c:\temp\access_log2 | ForEach-Object { $_.split(' ')[0] } |
Group-Object -NoElement |
Sort-Object Count
    
por 10.08.2017 / 19:38