PowerShell处理Nessus Essentials标准输出错误问题
解决PowerShell捕获Nessus Essentials未连接管理器状态的问题
问题描述
尝试通过PowerShell捕获Nessus Essentials的运行状态,目标是当软件输出[warn] [scanner] Not linked to a manager时,控制台仅输出一行Not linked to a manager。但无论将-like改为-match,还是调整匹配字符串(加/不加通配符、缩短字符串),均无法匹配成功,反而会输出多行冗余错误信息。
问题分析
- 运算符使用错误:
- 使用
-like时未添加通配符*,-like依赖通配符实现模糊匹配,直接写完整字符串仅会匹配完全一致的内容。 - 使用
-match时,字符串中的[和]是正则元字符,未转义会导致匹配逻辑失效。
- 使用
- 输出流判断偏差:Nessus可能将警告信息输出到
StdErr而非StdOut,原代码仅检查StdOut会遗漏该情况。 - 多行内容匹配问题:
$agentStatus.stdOut可能包含多行文本,直接用整串匹配会因换行符或额外内容导致失败。 - 错误抛出方式不当:使用
Throw会触发PowerShell错误处理机制,输出错误堆栈信息,造成冗余内容。
修复方案
- 同时检查StdOut和StdErr:确保不会遗漏输出到错误流的警告信息。
- 正确使用匹配运算符:
- 用
-like时添加通配符:*[warn] [scanner] Not linked to a manager* - 用
-match时转义元字符:\[warn\] \[scanner\] Not linked to a manager,或用[regex]::Escape()自动转义特殊字符
- 用
- 采用包含匹配逻辑:无需匹配整串内容,只需判断目标字符串是否存在于输出中。
- 替换Throw为Write-Host:若仅需输出提示信息而非终止脚本,用
Write-Host替代Throw避免错误堆栈输出。
完整修正代码
Function Start-ProcessGetStreams { [CmdLetBinding()] Param( [System.IO.FileInfo]$FilePath, [string[]]$ArgumentList ) $pInfo = New-Object System.Diagnostics.ProcessStartInfo $pInfo.FileName = $FilePath $pInfo.Arguments = $ArgumentList $pInfo.RedirectStandardError = $true $pInfo.RedirectStandardOutput = $true $pinfo.UseShellExecute = $false $pInfo.CreateNoWindow = $true $pInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden $proc = New-Object System.Diagnostics.Process $proc.StartInfo = $pInfo Write-Verbose "Starting $FilePath" $proc.Start() | Out-Null Write-Verbose "Waiting for $($FilePath.BaseName) to complete" $proc.WaitForExit() $stdOut = $proc.StandardOutput.ReadToEnd() $stdErr = $proc.StandardError.ReadToEnd() $exitCode = $proc.ExitCode Write-Verbose "Standard Output: $stdOut" Write-Verbose "Standard Error: $stdErr" Write-Verbose "Exit Code: $exitCode" [PSCustomObject]@{ "StdOut" = $stdOut "Stderr" = $stdErr "ExitCode" = $exitCode } } Function Get-NessusStatsFromStdOut { Param( [string]$stdOut ) $stats = New-Object System.Collections.Hashtable $StdOut -split "`r`n" | % { if($_ -like "*:*") { $result = $_ -split ":" $stats.add(($result[0].Trim() -replace "[^A-Za-z0-9]","_").ToLower(),$result[1].Trim()) } } Return $stats } Function Get-DateFromEpochSeconds { Param( [int]$seconds ) $utcTime = (Get-Date 01.01.1970)+([System.TimeSpan]::fromseconds($seconds)) Return Get-Date $utcTime.ToLocalTime() -Format "yyyy-MM-dd HH:mm:ss" } Try { $nessusExe = Join-Path $env:ProgramFiles -ChildPath "Tenable\Nessus\nessuscli.exe" -ErrorAction Stop } Catch { Write-Host "Cannot find NessusCli.exe" exit 1 } Write-Host "Getting Agent Status..." $agentStatus = Start-ProcessGetStreams -FilePath $nessusExe -ArgumentList "managed status" $notLinkedMsg = "Not linked to a manager" $warnPattern = "\[warn\] \[scanner\] $notLinkedMsg" If($agentStatus.stdOut -eq "" -and $agentStatus.StdErr -eq "") { Write-Host "No Data Returned from NessusCli" exit 1 } elseif($agentStatus.StdErr -match $warnPattern -or $agentStatus.stdOut -match $warnPattern) { Write-Host $notLinkedMsg exit 0 } elseif($agentStatus.StdOut -eq "" -and $agentStatus.StdErr -ne "") { Write-Host "StdErr: $($agentStatus.StdErr)" exit 1 } elseif(-not($agentStatus.stdOut -like "*Running: *")) { Write-Host "StdOut: $($agentStatus.StdOut)" exit 1 } else { $stats = Get-NessusStatsFromStdOut -stdOut $agentStatus.StdOut If($stats.last_connection_attempt -as [int]) { $stats.last_connection_attempt = Get-DateFromEpochSeconds $stats.last_connection_attempt } If($stats.last_connect -as [int]) { $stats.last_connect = Get-DateFromEpochSeconds $stats.last_connect } If($stats.last_scanned -as [int]) { $stats.last_scanned = Get-DateFromEpochSeconds $stats.last_scanned } $stats | Out-Host }
关键修改点
- 新增
$warnPattern变量,用转义后的正则表达式匹配警告信息 - 同时检查
StdOut和StdErr两个流的内容,避免遗漏警告 - 用
Write-Host替换Throw,消除错误堆栈的冗余输出 - 添加
exit命令控制脚本流程,匹配到目标状态后直接退出
内容的提问来源于stack exchange,提问作者T-Heron
相关产品推荐
相关产品推荐

