【问题标题】:Try/Catch or If/Else?Try/Catch 还是 If/Else?
【发布时间】:2015-12-14 13:47:23
【问题描述】:

大家好,我在试图弄清楚如何使这个脚本工作时遇到了麻烦,我对脚本非常陌生,但我确实了解其中的大部分内容,但仍在弄清楚一些事情。

try {
    Test-Connection -Computername $_ -count 1 -ErrorAction Stop
} catch {
    $_.Exception.ErrorCode -eq 0x800706ba
} `
{
    $err = 'Unavailable (Host Offline or Firewall)'
}

try {
    Test-UserCredentials -Username testuser -Password (Read-Host -AsSecureString)
} catch {
    $_.CategoryInfo.Reason -eq 'UnauthorizedAccessException'
} `
{
    $err = 'Access denied (Check User Permissions)'
}

Write-Warning "$computer- $err" | Out-File -FilePath c:\temp\Folder\Errors.txt -Append

我正在寻找的是这个脚本来测试系统是否响应。如果为 True,则下一步是测试凭据,最后是执行 get-wmiobject 查询。但是,如果系统不响应 ping,那么我想捕获未能响应 ping 的主机名,捕获它并将其导出到 txt 并在凭据失败时执行相同操作。

【问题讨论】:

  • 你好,为什么你的scriptBlockes里面有多个catches?
  • 我是个菜鸟,所以我现在知道我的极限。我从我找到的一个脚本中得到了这些脚本块,它使用了 try catch 和其他东西,这真的让我感到困惑,但至少可以正常工作。

标签: powershell if-statement try-catch credentials


【解决方案1】:

try..catch 用于处理终止错误。不要通过在不需要时强制检查失败来滥用它进行状态检查。如果您只想测试系统的可用性,请使用参数-Quiet 作为if 条件运行Test-Connection

if (Test-Connection -ComputerName $_ -Count 1 -Quiet) {
  ...
}

如果您需要级联多个检查,您可以通过反转检查并返回适当的消息以更易读的方式这样做:

function Test-Multiple {
  ...
  if (-not (Test-Connection -ComputerName $_ -Count 1 -Quiet)) {
    return "Host $_ unavailable."
  }

  $pw = Read-Host -AsSecureString
  if (-not (Test-UserCredentials -Username testuser -Password $pw)) {
    return 'Login failed for user testuser.'
  }
  ...
}

如果您想在日志文件中获取有关 ping 或登录失败的信息,您可以将其附加到相应的文件中:

function Test-Multiple {
  ...
  if (-not (Test-Connection -ComputerName $_ -Count 1 -Quiet)) {
    $_ | Add-Content 'C:\path\to\unavailable.log'
    return
  }

  $pw = Read-Host -AsSecureString
  if (-not (Test-UserCredentials -Username testuser -Password $pw)) {
    $_ | Add-Content 'C:\path\to\login_failure.log'
    return
  }
  ...
}

【讨论】:

  • 谢谢,但在此之后,我想导出无法 ping 或拒绝访问 txt 或 csv 文件的计算机,失败时返回错误。
  • @JoseAntonioChan 没有什么不同。请参阅更新的答案。如果您需要实际状态代码(例如用于连接测试),请使用 BaconBits' suggestion
【解决方案2】:

就我个人而言,我无法忍受Test-Connection 的行为。当它没有成功 ping 时抛出异常不是我想要的行为。就像,永远。我理解他们为什么这样做,但这不是我曾经希望 ping 工作的方式。 Test-Path 在路径无效时不会抛出异常。它只返回false。为什么Test-Connection这么不友好?

WMI 允许您捕获实际的状态代码,它还允许您轻松控制超时,因此它会更快地运行。

我倾向于使用这个:

$Ping = Get-WmiObject -Class Win32_PingStatus -Filter "Address='$ComputerName' AND Timeout=1000";
if ($Ping.StatusCode -eq 0) {
    # Success
}
else {
    # Failure
}

如果我真的想解码 ping 状态码:

$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'
    };

$Ping = Get-WmiObject -Class Win32_PingStatus -Filter "Address='$ComputerName' AND Timeout=1000"

$StatusCodes[$Ping.StatusCode];

【讨论】:

    【解决方案3】:

    你可以这样做:

    if(Test-Connection -Computername $_ -Count 2 -ErrorAction 0 -Quiet) {
        if(-not (Test-UserCredentials -Username testuser -Password (Read-Host -AsSecureString))) {
            $err = "Access denied (Check User Permissions)"
        }
    } else {
        $err = "Unavailable (Host Offline or Firewall)"
    }
    
    if($err) {
        Write-Warning "$computer - $err" | Out-File -FilePath c:\temp\Folder\Errors.txt -Append
    }
    

    我相信 Test-ConnectionTest-Credentials 是为了返回 $true 或 $false 而不是异常(如果使用得当),所以你在这里并不需要 try/catch

    【讨论】: