如何使用 -AsJob 开关接收测试连接中的错误

How to Receive Errors in Test-Connection with -AsJob switch

我正在设置一个监控脚本,它会 ping IP 并在数据包失败时发送电子邮件。我决定使用 Test-Connection。下面是一个示例代码:

代码1

$IPList = @("192.168.0.1","172.217.161.15")
Test-Connection -ComputerName $IPList -BufferSize 4 -Count 1 -AsJob -ErrorAction Stop
Get-Job | Wait-Job | Receive-Job

所以我得到的结果是:

Source        Destination     IPV4Address      IPV6Address                              Bytes    Time(ms) 
------        -----------     -----------      -----------                              -----    -------- 
BLR-AA200906  172.217.161.15                                                            4        55       
BLR-AA200906  192.168.0.1                                                               4                 

192系列IP应该会报错。您可以看到 Time(ms) 列是空的。

代码2

$IPList = @("192.168.0.1","172.217.161.15")
foreach ($IP in $IPList)
{
    Start-job -ScriptBlock {Test-Connection -ComputerName $Args[0] -BufferSize 4 -Count 1 -ErrorAction Stop} -ArgumentList $IP
}
Get-Job | Wait-Job | Receive-Job

如果我这样做,它会抛出一个我可以轻松捕捉到的错误

Testing connection to computer '192.168.0.1' failed: Error due to lack of resources
    + CategoryInfo          : ResourceUnavailable: (192.168.0.1:String) [Test-Connection], PingException
    + FullyQualifiedErrorId : TestConnectionException,Microsoft.PowerShell.Commands.TestConnectionCommand
    + PSComputerName        : localhost

问题

这引出了我的问题。我如何 receive/catch 在 Code1 中抛出错误消息?不同之处在于我使用的是 -AsJob,这比在 foreach 循环中启动作业要高效得多。

PS版本是5.1

重要的是要区分您的代码示例正在做不同的事情,Code1 正在启动一个作业和 运行 x 个测试连接。 Code2 正在启动 x 个作业,每个作业测试 1 个连接。

我不知道有什么方法可以使用 -asjob 消除测试连接中的错误,但是另一种方法是使用 statuscode 属性。 11010Error due to lack of resources 的状态码,当测试连接没有收到来自主机的响应时,即 Request Timed Out.

使用状态码的缺点是您需要翻译状态码。

$IPList = @("192.168.254.1","172.217.161.15")

$StatusCodes = @{
    [uint32]0     = 'Success'
    [uint32]11001 = 'Buffer Too Small'
    [uint32]11002 = 'Destination Net Unreachable'
    [uint32]11003 = 'Destination Host Unreachable'
    [uint32]11004 = 'Destination Protocol Unreachable'
    [uint32]11005 = 'Destination Port Unreachable'
    [uint32]11006 = 'No Resources'
    [uint32]11007 = 'Bad Option'
    [uint32]11008 = 'Hardware Error'
    [uint32]11009 = 'Packet Too Big'
    [uint32]11010 = 'Request Timed Out'
    [uint32]11011 = 'Bad Request'
    [uint32]11012 = 'Bad Route'
    [uint32]11013 = 'TimeToLive Expired Transit'
    [uint32]11014 = 'TimeToLive Expired Reassembly'
    [uint32]11015 = 'Parameter Problem'
    [uint32]11016 = 'Source Quench'
    [uint32]11017 = 'Option Too Big'
    [uint32]11018 = 'Bad Destination'
    [uint32]11032 = 'Negotiating IPSEC'
    [uint32]11050 = 'General Failure'
}

Test-Connection -ComputerName $IPList -BufferSize 4 -Count 1 -AsJob
Get-Job | Wait-Job | Receive-Job | ft Address,IPV4Address,IPV6Address,Buffersize,ResponseTime,@{n="Status";e={$StatusCodes[$_.statuscode]}}

这给出了以下输出

Address        IPV4Address    IPV6Address Buffersize ResponseTime Status           
-------        -----------    ----------- ---------- ------------ ------           
172.217.161.15 172.217.161.15                      4          381 Success          
192.168.254.1                                      4              Request Timed Out

我很感激这不会给你错误消息,但你可以检查每个请求的状态并使用它来执行你本可以通过捕获错误来完成的业务逻辑。

参考资料

IcmpSendEcho2 fails with fails with WSA_QOS_ADMISSION_FAILURE and ERROR_NOACCESS

https://docs.microsoft.com/en-gb/windows/desktop/api/ipexport/ns-ipexport-icmp_echo_reply

我没有特定的解决方案,但这是导致您出现问题的原因:

-AsJob 参数创建一个 WMI 作业,而 Start-Job 创建一个 CIM 作业。

WMI 作业每个都有自己的实例,而 CIM 作业 运行 在您当前的 Powershell 实例中。因此,WMI 作业不会花费额外的时间来使用当前会话创建和销毁实例,而这可能会非常昂贵。

WMI 作业通常与 Powershell 有点分离。他们似乎没有 "host" shell 可以转录。这就是 Powershell 的双重性可能会让人有点困惑的地方。

不幸的是,在这种情况下,这意味着您完全依赖 Test-Connection cmdlet 实现 Powershell 的事件处理程序,但事实并非如此,因为作业状态总是记录作为 'Completed'。您无法再通过简单地将自己的 shell 转储到输出流来回退到 CIM 作业带来的 "compatibility"。

Start-Job -ScriptBlock {Write-Host "This is not real output."} | Wait-Job | Receive-Job
PS> This is not real output.

因此,在你的情况下,如果你必须使用 -AsJob,你能做的最好的事情就是监视 $error。 (据我所知。)