我编写了一个脚本来帮助我分析访问日志文件,而无需通过 SSH 连接到服务器(我只有文件)。它计算并排序访问我管理的站点的 IP 地址数量,但我发现它在处理大型文件时花费的时间太长(它非常简陋)。我不想使用编译的应用程序,也没有通过 SSH 连接到服务器,所以我转向了 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
我也试过
$regex = ‘\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b’
对于 5MB 文件(14.4K 行),需要 18 分钟 对于 37MB 文件(158.5K 行),需要 3 个多小时
秒表仅供我测试。脚本只是提取 IP、计数并按出现次数排序。也许文件写入是最大的减慢因素,但我对存储在 RAM 中的变量不太熟悉。我认为有更好的方法来提取 IP 地址(也许每行只使用前 15 个字符?)。以下是行的示例,组合日志格式
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/)"
有什么建议吗?
答案1
如果每行都以 ip 开头,则有一个更简单(希望更快)的方法。在空格处进行简单的拆分即可将 ip 分开。
在处理这些行时,你可以增加一个由 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
仅复制您的行的示例输出:
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
答案2
为什么要将数据写入中间文件 ( $ip_file
)?为什么不将其直接从 Group-Object 传递?也许像这样?
$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
如果这不能使事情变得更快,那么你应该尝试找出脚本中哪部分是比较慢的。
也许可以先做这样的事情。然后一点一点地添加功能,看看速度慢在哪里。
$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
如果您愿意假设日志文件始终格式正确,那么您可以加快速度并只抓取第一列。(这在我的计算机上处理了一个 233MB、1,000,749 行的 Apache 日志文件,大约需要 15 秒)
Get-Content c:\temp\access_log2 | ForEach-Object { $_.split(' ')[0] } |
Group-Object -NoElement |
Sort-Object Count