第6天:文本处理、正则表达式与日志分析实战

🎯 学习目标

  • 掌握 PowerShell 中核心文本处理 cmdlet:Select-StringConvertFrom-StringDataOut-String 等
  • 熟练使用正则表达式(RegEx)进行匹配、提取与替换
  • 能够高效分析 Windows 事件日志、IIS 日志、自定义日志文件
  • 补充理解前几日关键语法细节:$_、计算属性、.Where() 方法等

一、文本处理基础 cmdlet

1. Get-Content:读取文件内容

# 读取整个文件为字符串数组(每行一个元素)
$lines = Get-Content .\app.log

# 读取为单个字符串(适合正则全文匹配)
$text = Get-Content .\app.log -Raw

💡 -Raw 参数在处理多行模式正则时非常关键。



2. Select-String:PowerShell 的 “grep”

# 查找包含 "ERROR" 的行(默认不区分大小写)
Select-String -Path .\app.log -Pattern "ERROR"

# 使用正则 + 区分大小写
Select-String -Path .\app.log -Pattern "\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b" -CaseSensitive

# 输出上下文(前后各2行)
Select-String -Path .\app.log -Pattern "Exception" -Context 2,2

🔍 返回的是 MatchInfo 对象,包含 LineNumberLineMatches 等属性。


3. Out-String 与 ToString()

当需要将对象转为可读文本(而非对象流)时使用:

Get-Process | Select-Object Name, CPU | Out-String

⚠️ 注意:这会破坏对象结构,仅用于显示或写入日志,不要用于后续处理


二、正则表达式(RegEx)深度使用

PowerShell 原生支持 .NET 正则引擎([regex] 类)。

1. 基本匹配:-match 操作符

"2025-11-15 14:30:00 ERROR User login failed" -match "ERROR"
# 返回 True/False,匹配结果存于自动变量 $matches

if ("user@example.com" -match "(\w+)@(.+)") {
    Write-Host "用户名: $($matches[1])"
    Write-Host "域名: $($matches[2])"
}

$matches 是哈希表,$matches[0] 是完整匹配,[1], [2]... 是捕获组。


2. 提取所有匹配项:[regex]::Matches()

$log = Get-Content .\access.log -Raw
$ipPattern = "\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b"
$ips = [regex]::Matches($log, $ipPattern) | ForEach-Object { $_.Value }
$ips | Sort-Object -Unique

3. 替换文本:-replace

# 隐藏 IP 地址后两段
"Client IP: 192.168.1.100" -replace "(\d+\.\d+)\.\d+\.\d+", '$1.X.X'
# 输出:Client IP: 192.168.X.X

# 使用脚本块动态替换(高级)
$text = "Price: $100, Tax: $10"
$text -replace '\$(\d+)', { "$" + ([int]$args[0].Groups[1].Value * 0.9) }
# 将金额打9折(需 PowerShell 6+ 支持脚本块替换)

⚠️ 在 PowerShell 5.1 及以下,-replace 不支持脚本块,需用 [regex]::Replace()


三、日志分析实战案例

案例1:分析 Windows 事件日志(系统错误)

# 获取最近10条系统错误事件
Get-WinEvent -LogName System -MaxEvents 10 |
Where-Object { $_.Level -eq 2 } |  # Level 2 = Error
Select-Object TimeCreated, Id, Message |
Format-List

🔍 补充:Level 含义:

  • 1: Critical
  • 2: Error
  • 3: Warning
  • 4: Information

案例2:解析 IIS 日志(W3C 格式)

假设日志头为:

#Fields: date time s-ip cs-method cs-uri-stem sc-status
2025-11-15 14:30:00 192.168.1.10 GET /index.html 200
2025-11-15 14:31:00 192.168.1.10 POST /login 401
# 跳过注释行,按空格分割
$logs = Get-Content .\u_ex251115.log |
Where-Object { $_ -notlike "#*" } |
ForEach-Object {
    $fields = $_ -split ' '
    [PSCustomObject]@{
        Date   = $fields[0]
        Time   = $fields[1]
        Method = $fields[4]
        URI    = $fields[5]
        Status = [int]$fields[6]
    }
}

# 查找所有 401(未授权)请求
$logs | Where-Object { $_.Status -eq 401 }

💡 进阶:可用 ConvertFrom-String(PowerShell 5+)配合模板解析复杂日志。


案例3:监控应用日志中的异常堆栈

$logFile = "C:\App\error.log"
$recentErrors = Get-Content $logFile -Tail 100  # 仅读最后100行(高效!)

if ($recentErrors -match "System\.NullReferenceException") {
    Write-Host "发现空引用异常!" -ForegroundColor Red
    # 可触发告警、发送邮件等
}

-Tail 参数避免加载大文件,极大提升性能。


四、补充:前几日关键语法细节详解

1. $_ 到底是什么?

  • 在 Where-Object { $_.CPU -gt 100 } 中,$_ 表示当前管道中传递的每个对象
  • 它是 $_ = $PSItem 的别名(PowerShell 3.0+ 引入 $PSItem 更清晰)。
  • 本质是 .NET 对象,所以可直接访问属性/方法。

✅ 等价写法:

Get-Process | Where-Object { $PSItem.CPU -gt 100 }

2. 计算属性(Calculated Property)语法

Select-Object Name, @{Name="MemoryMB"; Expression={ [math]::Round($_.WS / 1MB, 2) }}
  • @{} 创建哈希表
  • Name(或 Label)指定输出列名
  • Expression 是脚本块,$_ 代表当前对象

💡 也可简写为:

Select-Object Name, @{n="MemoryMB"; e={ $_.WS / 1MB }}

3. .Where() 和 .ForEach() 方法(PowerShell 4.0+)

Where-Object 更快(尤其大数据集):

$procs = Get-Process
$highCPU = $procs.Where({ $_.CPU -gt 100 })  # 返回数组
$names = $procs.ForEach({ $_.ProcessName })   # 返回名称数组

✅ 优势:无需管道,直接调用 .NET 方法,性能更高。


五、今日重点总结

  • ✅ Select-String 是日志搜索利器,支持上下文和正则
  • ✅ 正则用 -match[regex]::Matches()-replace 三件套
  • ✅ 分析日志时优先用 -Tail-Raw 控制内存
  • ✅ $_ = 当前对象,计算属性 = @{Name=...; Expression=...}
  • ✅ .Where() / .ForEach() 比管道更快,适合本地数组处理

📚 参考资料

  • About Comparison Operators
  • Select-String Documentation
  • .NET Regex Class

🏁 课后作业

  1. 编写脚本:读取 C:\Windows\setupact.log(如有),找出所有包含 "Driver" 的行,并提取驱动包名称(通常在 Installing driver package: XXX 中)。
  2. 使用正则表达式从一段文本中提取所有邮箱地址,并去重。
  3. 对比性能:用 Where-Object 和 .Where() 分别过滤 10,000 个进程对象(模拟),观察执行时间差异(用 Measure-Command)。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐