适用于 PowerShell 5.1 及以上版本(Windows)
传统的自动化脚本是”拉模型”——定时轮询检查状态再执行操作。事件驱动是”推模型”——当特定事件发生时自动触发处理逻辑。Windows 和 PowerShell 提供了丰富的事件机制:WMI 事件、.NET 对象事件、文件系统变更事件、Windows 事件日志事件。掌握事件驱动编程,可以构建响应迅速、资源高效的自动化系统。
本文将讲解 PowerShell 中的事件驱动模式及其在运维自动化中的应用。
WMI 事件订阅 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 $query = "SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'" Register-WmiEvent -Query $query -SourceIdentifier "ProcessCreated" -Action { $proc = $Event .SourceEventArgs.NewEvent.TargetInstance $name = $proc .Name $pid = $proc .ProcessId $time = Get-Date -Format 'HH:mm:ss' Write-Host "[$time ] 新进程:$name (PID: $pid )" -ForegroundColor Green if ($name -in @ ("cmd.exe" , "powershell.exe" , "pwsh.exe" )) { Write-Host " 警告:检测到命令行进程启动" -ForegroundColor Yellow } } Write-Host "已订阅进程创建事件" -ForegroundColor CyanWrite-Host "打开新的命令行窗口测试..." -ForegroundColor DarkGray$serviceQuery = "SELECT * FROM __InstanceModificationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Service' AND TargetInstance.State <> PreviousInstance.State" Register-WmiEvent -Query $serviceQuery -SourceIdentifier "ServiceStateChanged" -Action { $svc = $Event .SourceEventArgs.NewEvent.TargetInstance $prev = $Event .SourceEventArgs.NewEvent.PreviousInstance $name = $svc .Name $newState = $svc .State $oldState = $prev .State $color = if ($newState -eq 'Running' ) { 'Green' } else { 'Red' } Write-Host "[$ (Get-Date -Format 'HH:mm:ss')] 服务变更:$name $oldState => $newState " -ForegroundColor $color } Write-Host "已订阅服务状态变更事件" -ForegroundColor Cyan
执行结果示例:
1 2 3 4 5 6 7 已订阅进程创建事件 打开新的命令行窗口测试... [08:30:15] 新进程:cmd.exe (PID: 12345 ) 警告:检测到命令行进程启动 [08:30:45] 新进程:notepad.exe (PID: 12346 )已订阅服务状态变更事件 [08:31:10] 服务变更:Spooler Running => Stopped
文件到达自动处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 function Start-FileArrivalProcessor { param ( [string ]$WatchPath = "C:\DropZone" , [string ]$ProcessedPath = "C:\DropZone\Processed" , [string ]$ErrorPath = "C:\DropZone\Errors" ) foreach ($path in @ ($WatchPath , $ProcessedPath , $ErrorPath )) { New-Item $path -ItemType Directory -Force | Out-Null } $watcher = New-Object System.IO.FileSystemWatcher $watcher .Path = $WatchPath $watcher .Filter = "*.csv" $watcher .EnableRaisingEvents = $true $action = { $filePath = $Event .SourceEventArgs.FullPath $fileName = $Event .SourceEventArgs.Name Start-Sleep -Seconds 2 Write-Host "[$ (Get-Date -Format 'HH:mm:ss')] 检测到新文件:$fileName " -ForegroundColor Cyan try { $data = Import-Csv $filePath -Encoding UTF8 $rows = $data .Count Write-Host " 数据行数:$rows " -ForegroundColor Green $destPath = Join-Path $ProcessedPath $fileName Move-Item $filePath $destPath -Force Write-Host " 已移至处理目录" -ForegroundColor Green } catch { $errorDest = Join-Path $ErrorPath $fileName Move-Item $filePath $errorDest -Force Write-Host " 处理失败:$ ($_ .Exception.Message)" -ForegroundColor Red } } Register-ObjectEvent -InputObject $watcher -EventName Created -Action $action -SourceIdentifier "FileArrival" Write-Host "文件到达处理器已启动:$WatchPath " -ForegroundColor Green } Start-FileArrivalProcessor
执行结果示例:
1 2 3 4 文件到达处理器已启动:C:\DropZone [08:35:10] 检测到新文件:orders-20250828.csv 数据行数:150 已移至处理目录
定时事件与调度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 function Start-PeriodicCheck { param ( [scriptblock ]$CheckScript , [int ]$IntervalSeconds = 60 , [string ]$Name = "PeriodicCheck" ) $timer = New-Object System.Timers.Timer $timer .Interval = $IntervalSeconds * 1000 $timer .AutoReset = $true $action = { try { & $CheckScript } catch { Write-Host "[$ (Get-Date -Format 'HH:mm:ss')] 检查异常:$ ($_ .Exception.Message)" -ForegroundColor Red } } Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action $action -SourceIdentifier $Name $timer .Start() Write-Host "定时检查已启动:$Name (间隔 $ {IntervalSeconds}s)" -ForegroundColor Green return $timer } $diskTimer = Start-PeriodicCheck -Name "DiskSpaceCheck" -IntervalSeconds 60 -CheckScript { $disk = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='C:'" $freeGB = [math ]::Round($disk .FreeSpace / 1 GB, 2 ) $usedPct = [math ]::Round(($disk .Size - $disk .FreeSpace) / $disk .Size * 100 , 1 ) if ($usedPct -gt 90 ) { Write-Host "[$ (Get-Date -Format 'HH:mm:ss')] 磁盘告警!C盘使用率 $usedPct %,剩余 $freeGB GB" -ForegroundColor Red } else { Write-Host "[$ (Get-Date -Format 'HH:mm:ss')] C盘正常:$usedPct %(剩余 $freeGB GB)" -ForegroundColor Green } } $svcTimer = Start-PeriodicCheck -Name "ServiceCheck" -IntervalSeconds 30 -CheckScript { $services = @ ("W3SVC" , "MSSQLSERVER" , "MyApp" ) foreach ($svcName in $services ) { $svc = Get-Service $svcName -ErrorAction SilentlyContinue if ($svc -and $svc .Status -ne 'Running' ) { Write-Host "[$ (Get-Date -Format 'HH:mm:ss')] $svcName 异常:$ ($svc .Status)" -ForegroundColor Red } } }
执行结果示例:
1 2 3 4 定时检查已启动:DiskSpaceCheck (间隔 60 s) 定时检查已启动:ServiceCheck (间隔 30 s) [08 :31 :00 ] C 盘正常:72.4 [08 :31 :30 ] [08 :32 :00 ] C 盘正常:72.4
Windows 事件日志触发 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 function Watch-EventLog { param ( [string ]$LogName = "Application" , [int ]$EventId , [string ]$Source , [string ]$Level = "Error" ) $query = "*[System/Level=$ (switch ($Level ) { 'Critical' { 1 } 'Error' { 2 } 'Warning' { 3 } 'Info' { 4 } })]" if ($EventId ) { $query = "*[System/EventID=$EventId ]" } Register-WmiEvent -Query @" SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_NTLogEvent' AND TargetInstance.LogFile = '$LogName ' "@ -SourceIdentifier "EventLog_$LogName " -Action { $evt = $Event .SourceEventArgs.NewEvent.TargetInstance $time = $evt .TimeGenerated $source = $evt .SourceName $msg = $evt .Message if ($msg .Length -gt 100 ) { $msg = $msg .Substring(0 , 100 ) + "..." } Write-Host "[$time ] 事件:$source " -ForegroundColor Yellow Write-Host " $msg " -ForegroundColor DarkGray } Write-Host "已订阅事件日志:$LogName " -ForegroundColor Green } Watch-EventLog -LogName "Application" -Level "Error" Write-Host "`n当前事件订阅:" -ForegroundColor CyanGet-EventSubscriber | Select-Object SourceIdentifier, SubscriptionId | Format-Table -AutoSize
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 ----------------- --------------
注意事项
事件队列 :PowerShell 事件存储在内存队列中,长时间运行可能积压大量事件,定期用 Get-Event 处理
资源释放 :事件订阅和 FileSystemWatcher 使用完毕后必须清理,否则会持续消耗资源
WITHIN 间隔 :WMI 事件的 WITHIN 参数控制轮询间隔,间隔越短 CPU 使用越高
事件丢失 :高频事件可能被丢弃(缓冲区溢出),关键场景需要补充轮询机制
会话限制 :事件订阅只在当前 PowerShell 会话中有效,需要持久化请使用计划任务或 Windows 服务
权限要求 :某些 WMI 事件订阅需要管理员权限