#日志统计
#题目
给定一个文本日志文件,每行包含日期、请求方法、路径、状态码和耗时,例如:
2026-11-01 10:00:01 GET /api/users 200 12ms
2026-11-01 10:00:02 POST /api/login 401 8ms
2026-11-01 10:00:03 GET /api/users 200 9ms
2026-11-01 10:00:04 GET /api/missing 404 3ms
2026-11-01 10:00:05 POST /api/login 500 25ms
统计各状态码出现的次数,并按次数从高到低排序。
#要求
- 接收日志文件路径作为参数。
- 使用正则表达式提取三位数字的状态码。
- 输出每个状态码及其出现次数。
- 结果按次数降序排列,次数相同时按状态码升序排列。
#参考实现
status-report.ps1
param(
[Parameter(Mandatory)]
[string]$Path
)
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
Write-Error "文件不存在:$Path"
exit 1
}
Get-Content -LiteralPath $Path |
Select-String -Pattern '\s(?<Status>[1-5]\d{2})\s' |
ForEach-Object { $_.Matches[0].Groups['Status'].Value } |
Group-Object |
Sort-Object -Property @{ Expression = 'Count'; Descending = $true },
@{ Expression = 'Name'; Descending = $false } |
Select-Object Name, Count
运行:
> ./status-report.ps1 -Path ./access.log
Name Count
---- -----
200 152
404 23
500 7
#讲解
#Select-String
Select-String 按正则表达式查找匹配行,默认返回 MatchInfo 对象:
> Get-Content ./access.log | Select-String -Pattern '\s(?<Status>[1-5]\d{2})\s'
(?<Status>...)是命名捕获组,便于后面取值。[1-5]\d{2}匹配 100 到 599 的状态码,避免误匹配日期或耗时中的数字。- 匹配结果保存在
$_.Matches中,Groups['Status'].Value取出捕获内容。
#Group-Object
Group-Object 把相同值的对象归为一组,默认按对象本身分组:
> '200', '404', '200' | Group-Object
对于字符串,Name 是分组的值,Count 是该值出现的次数。也可以用 -Property 显式指定分组属性。
#Sort-Object
Sort-Object 按一个或多个属性排序:
> $groups | Sort-Object -Property @{ Expression = 'Count'; Descending = $true },
> @{ Expression = 'Name'; Descending = $false }
- 多属性排序时,先按
Count排序,Count相同时再按Name排序。 - 哈希表形式可以分别指定每个属性的排序方向;直接写
Count, Name -Descending会让两个属性都降序。
最后用 Select-Object Name, Count 只保留需要的列,输出更清晰。