约 2258 字

约 11 分钟

#日志统计

#题目

给定一个文本日志文件,每行包含日期、请求方法、路径、状态码和耗时,例如:

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 只保留需要的列,输出更清晰。

#推荐阅读

更新: 2026/11/2

作者: planc

创建: 2026/11/2