如何用System.IO的FileStream替代PowerShell的Get-Content cmdlet?
问题解决:PowerShell日志监控脚本中StreamReader替代Get-Content的错误修复
问题背景
原本的PowerShell脚本通过Get-Content -Tail 1读取日志文件最后一行,判断是否包含"Finished."来触发邮件告警。尝试改用System.IO.StreamReader实现读取逻辑后,出现路径为空、无法调用空值方法的错误,报错信息如下:
MethodInvocationException: C:\monfile.ps1:15 Line | 15 | $reader = [System.IO.StreamReader]::new($path) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | Exception calling ".ctor" with "1" argument(s): "The value cannot be an empty string. (Parameter 'path')" InvalidOperation: C:\monfile.ps1:17 Line | 17 | while($null -ne ($line = $reader.ReadLine())) { | ~~~~~~~~~~~~~~~~~~~~~~~~~~ | You cannot call a method on a null-valued expression. InvalidOperation: C:\monfile.ps1:22 Line | 22 | $reader.Dispose() | ~~~~~~~~~~~~~~~~~ | You cannot call a method on a null-valued expression.
错误原因
- $Event变量作用域错误:
$Event.SourceEventArgs.FullPath仅在Register-ObjectEvent的-Action脚本块内有效,主循环中无法获取这个变量的值,导致$logFile为空字符串,传递给Read-LastLine时触发路径为空的错误。 - 资源管理不严谨:原
Read-LastLine函数未处理StreamReader初始化失败的情况,导致后续调用ReadLine()和Dispose()时出现空值调用错误。
修复后的脚本
$fromaddress = "filemon@contoso.com" $emailto = "IT@contoso.com" $SMTPSERVER = "xx.xx.xx.xx" $global:FileChanged = $false $folder = "C:\tmp" $filter = "log.log" $logFilePath = Join-Path -Path $folder -ChildPath $filter # 初始化文件监控器 $watcher = New-Object IO.FileSystemWatcher $folder,$filter -Property @{ IncludeSubdirectories = $false EnableRaisingEvents = $true } Register-ObjectEvent $Watcher "Changed" -Action {$global:FileChanged = $true} > $null function Read-LastLine ([string]$Path) { # 参数校验:路径为空或文件不存在直接返回空 if ([string]::IsNullOrWhiteSpace($Path) -or -not (Test-Path -Path $Path -PathType Leaf)) { return $null } $lastLine = '' # 使用using语句自动释放StreamReader资源,无需手动Dispose try { using ($reader = [System.IO.StreamReader]::new($Path)) { while ($null -ne ($line = $reader.ReadLine())) { $lastLine = $line } } } catch { Write-Warning "读取文件失败:$_" return $null } return $lastLine } while ($true) { while (-not $global:FileChanged) { Start-Sleep -Milliseconds 100 } $lastLine = Read-LastLine -Path $logFilePath if ($lastLine -match "Finished\.") { # 转义.避免正则匹配任意字符 Write-Host "邮件发送中" Send-MailMessage -From $fromaddress -To $emailto -Subject "日志已完成" -SmtpServer $SMTPSERVER -Encoding UTF8 -Priority High } # 重置标记,继续监控 $global:FileChanged = $false }
关键修改说明
- 固定文件路径:直接用
Join-Path构造监控的日志文件路径,避免依赖$Event变量的作用域问题。 - 参数校验与错误处理:在
Read-LastLine中先校验路径有效性,加入try/catch处理文件读取异常(比如文件被锁定)。 - 自动资源管理:使用
using语句创建StreamReader,PowerShell会自动在代码块结束后释放资源,避免手动Dispose可能出现的空值错误。 - 正则转义:判断时用
Finished\.代替Finished.,因为-match是正则匹配,.默认匹配任意字符,转义后才会匹配实际的点号。
内容的提问来源于stack exchange,提问作者Arbelac
相关产品推荐
相关产品推荐

