You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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.

错误原因

  1. $Event变量作用域错误:$Event.SourceEventArgs.FullPath仅在Register-ObjectEvent的-Action脚本块内有效,主循环中无法获取这个变量的值,导致$logFile为空字符串,传递给Read-LastLine时触发路径为空的错误。
  2. 资源管理不严谨:原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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 00:52:53