📅 第6天重点总结回顾
| 技术点 | 用途 | 最佳实践 |
|---|---|---|
Select-String | 高效文本/日志搜索 | 用 -Pattern + -Context 查看前后行 |
| 正则表达式 | 模式匹配与提取 | 优先用 [regex]::Matches() 获取所有匹配组 |
-Tail / -Raw | 控制大文件内存占用 | 日志分析首选 -Tail 100 避免加载全文件 |
$_ 与计算属性 | 自定义输出结构 | Select-Object @{Name="X"; Expression={...}} |
.Where() 方法 | 本地数组高性能过滤 | 比 Where-Object 快 5–10 倍(无管道开销) |
🏁 课后作业答案与解析
作业1:从 setupact.log 提取驱动包名称
💡 路径:
C:\Windows\setupact.log(Windows 安装/更新日志,通常存在)
✅ 脚本:Get-DriverPackages.ps1
# 检查日志是否存在
$logPath = "C:\Windows\setupact.log"
if (-not (Test-Path $logPath)) {
Write-Warning "日志文件不存在: $logPath"
return
}
# 使用 Select-String 搜索包含 "Installing driver package:" 的行
# 并通过正则提取驱动包名(XXX 部分)
$pattern = 'Installing driver package:\s*(.+)'
# 为节省内存,仅读取最后 500 行(假设驱动安装在近期)
$matches = Get-Content -Path "C:\Windows\setupact.log" -Tail 500 |
Select-String -Pattern 'Installing driver package:\s*(.+)'
# 提取捕获组(即驱动包名称),去重并排序
$driverPackages = $matches |
ForEach-Object { $_.Matches.Groups[1].Value } |
Sort-Object -Unique
if ($driverPackages) {
Write-Host "找到 $($driverPackages.Count) 个唯一驱动包:" -ForegroundColor Green
$driverPackages
} else {
Write-Host "未找到驱动安装记录。" -ForegroundColor Yellow
}
🔍 关键解析:
- 流式处理(避免一次性加载所有 500 行到内存)
Get-Content -Path "C:\Windows\setupact.log" -Tail 500 |
Select-String -Pattern 'Installing driver package:\s*(.+)'
- 正则
(.+):捕获Installing driver package:后的所有内容 $_.Matches.Groups[1].Value:访问第一个捕获组(即 XXX)Sort-Object -Unique:自动去重并排序
✅ 示例输出:
oem123.inf usbccgp.inf wudfusb.inf
作业2:从文本中提取所有邮箱地址并去重
✅ 脚本:Extract-Emails.ps1
# 模拟一段含邮箱的文本(可替换为 Get-Content 读文件)
$text = @"
Contact us at support@example.com or sales@contoso.org.
Also reach out to admin@local, user.name+tag@domain.co.uk, and invalid-email.
For help: help@sub.domain.com
"@
# 使用 [regex]::Matches() 提取所有符合邮箱格式的字符串
$emailRegex = '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'
$allMatches = [regex]::Matches($text, $emailRegex)
# 提取值、转小写(标准化)、去重
$uniqueEmails = $allMatches.Value |
ForEach-Object { $_.ToLower() } |
Sort-Object -Unique
Write-Host "提取到 $($uniqueEmails.Count) 个唯一邮箱:" -ForegroundColor Cyan
$uniqueEmails
🔍 关键解析:
[regex]::Matches():返回所有匹配对象(比-match更适合多结果)- 正则说明:
[a-zA-Z0-9._%+-]+:用户名部分(允许常见符号)@:必须有 @[a-zA-Z0-9.-]+:域名(允许子域和连字符)\.[a-zA-Z]{2,}:顶级域(至少2字母)
.ToLower():避免User@Example.COM和user@example.com被视为不同
✅ 输出:
help@sub.domain.com sales@contoso.org support@example.com user.name+tag@domain.co.uk
⚠️ 注意:此正则不完美,但适用于大多数日志场景。如需严格校验,可集成 .NET 的
MailAddress类。
作业3:对比 Where-Object 与 .Where() 性能
✅ 脚本:Compare-FilterPerformance.ps1
# 模拟 10,000 个进程对象(避免真实 Get-Process 开销)
$processes = 1..10000 | ForEach-Object {
[PSCustomObject]@{
Name = "Process$_"
Id = Get-Random -Minimum 1000 -Maximum 99999
CPU = Get-Random -Minimum 0 -Maximum 100
}
}
# 方法1:使用 Where-Object(管道)
$time1 = Measure-Command {
$result1 = $processes | Where-Object { $_.CPU -gt 50 }
}
$count1 = $result1.Count
# 方法2:使用 .Where() 方法(本地数组方法)
$time2 = Measure-Command {
$result2 = $processes.Where({ $_.CPU -gt 50 })
}
$count2 = $result2.Count
# 输出对比
[PSCustomObject]@{
Method = "Where-Object (Pipeline)"
TimeMs = [math]::Round($time1.TotalMilliseconds, 2)
ResultCount = $count1
} | Format-List
[PSCustomObject]@{
Method = ".Where() (Method)"
TimeMs = [math]::Round($time2.TotalMilliseconds, 2)
ResultCount = $count2
} | Format-List
🔍 笔者笔记本输出:
PS XXXXXXXXXXXXX> .\test.ps1
Method : Where-Object (Pipeline)
TimeMs : 110.4
ResultCount : 4910
Method : .Where() (Method)
TimeMs : 42.45
ResultCount : 4910
💡 关键解析:
.Where()是 .NET 方法,直接操作数组,无管道创建/销毁开销Where-Object是 cmdlet,每次传递对象都经过 PowerShell 管道引擎- 适用场景:
- 小数据量(<100):差异可忽略
- 大数据量(>1000)或循环内:优先用
.Where()
📌 扩展:同理,
.ForEach({})比ForEach-Object更快。
✅ 第6天能力验证总结
| 作业 | 考察能力 | 实战价值 |
|---|---|---|
| 1 | Select-String + 正则提取 | 快速定位系统/应用日志中的关键事件 |
| 2 | 正则多匹配 + 去重标准化 | 数据清洗、安全审计(如提取凭证) |
| 3 | 性能敏感代码优化 | 编写高效自动化脚本,避免“慢脚本” |
🚀 进阶建议
- 对于超大日志(GB 级),考虑结合
Get-Content -ReadCount 1000分块处理 - 使用
[regex]::Escape()转义用户输入,防止正则注入 - 在函数中返回
[PSCustomObject]而非纯字符串,便于后续结构化处理
768

被折叠的 条评论
为什么被折叠?



