【问题标题】:Skip part of script on error出错时跳过部分脚本
【发布时间】:2018-12-08 02:42:39
【问题描述】:

我一直在用头撞墙一段时间。到目前为止,没有多少谷歌搜索给我带来任何成功的结果。想知道是否有人可以帮我一把?

# Load the PowerShell module for Active Directory
Import-Module ActiveDirectory

# For each computer in AD that is a member of the target group
Get-ADGroupMember -Identity "CN=RenameComputer,CN=Users,DC=int,DC=example,DC=com" | ForEach {

        # Define the new name as the old one, minus one letter at the end
        $NewComputerName = $_.Name -replace ".$"

        # Perform the rename operation
        Rename-Computer -ComputerName $_.Name -NewName $NewComputerName -Force -PassThru -Restart -WhatIf

        # Remove the computer from the target group
        # THIS SHOULD NOT OCCUR IF THE RENAME ABOVE FAILED!!!
        Remove-ADGroupMember -Identity "CN=RenameComputer,CN=Users,DC=int,DC=example,DC=com" -Members $NewComputerName -WhatIf

}

TL;DR: 此脚本在指定组中查找一组计算机,重命名它们,然后将它们从组中删除。如果重命名失败(机器离线等)或抛出一些错误,我需要帮助告诉脚本不要将它们从组中删除。

想法?

提前致谢!

【问题讨论】:

    标签: powershell if-statement foreach active-directory try-catch


    【解决方案1】:

    您可以通过多种方式在 Powershell 中检查 cmdlet 的结果。

    将 cmdlet 作为 if 语句条件运行

    您可以将 cmdlet 的返回对象用作if 语句条件,只要它在成功时返回一个对象(这就是为什么我们需要-PassThru 来代替Rename-Computer,因为通常它不会返回一个东西)。因此,以您的代码为例(并删除-WhatIf):

    # If Rename-Computer succeeds...
    if( Rename-Computer -ComputerName $_.Name `
      -NewName $NewComputerName -Force -PassThru -Restart ) {
    
      # then remove the Computer from group 
      Remove-ADGroupMember -Identity "CN=RenameComputer,CN=Users,DC=int,DC=example,DC=com" `
       -Members $NewComputerName
    
    }
    

    这是因为对象在 Powershell 中是真实的。大多数定义的对象和除0(零)以外的任何数字都被视为$True,但$null""(空字符串)和0(零)等值被评估为$False。因此,对于您的代码,如果成功,将返回一个对象($True),如果发生错误,则不会返回任何对象($False)。

    显式检查 cmdlet 结果

    或者,您可以先运行Rename-Computer,然后检查其是否成功:

    Rename-Computer -ComputerName $_.Name -NewName $NewComputerName -Force
    
    # Check success of last cmdlet
    if( $? ) {
      Remove-ADGroupMember -Identity "CN=RenameComputer,CN=Users,DC=int,DC=example,DC=com" `
       -Members $NewComputerName
    }
    

    $? 评估上次 cmdlet 运行是否成功(不要将此与 $LASTEXITCODE 混淆,$LASTEXITCODE 用于检查上次运行程序结果,而不是 cmdlet 结果)。如果 cmdlet 成功,则返回 $True,否则返回 $False

    Try/Catch 块中捕获错误

    第三种方法是使用try/catch 块,但您必须确保任何错误都终止(如果未捕获将停止脚本)以捕获异常。您可以为此使用-ErrorAction Stop 参数:

        try {
    
          Rename-Computer -ComputerName $_.Name -NewName $NewComputerName -Force `
            -PassThru -Restart -ErrorAction Stop
    
          Remove-ADGroupMember -Identity "CN=RenameComputer,CN=Users,DC=int,DC=example,DC=com" `
            -Members $NewComputerName
    
        } catch {
          # Do something on failure
          Write-Warning "An error occurred: $($_.Exception.Message)"
        }
    

    如果Rename-Computertry 块内抛出错误,它会在失败时,执行跳转到catch 块并跳过Remove-ADGroupMember。如果使用try/catch 是您首选的错误处理方法,您可以考虑在脚本中设置$ErrorActionPreference = 'Stop',这会将默认-ErrorAction 行为设置为Stop,并且您不必在每个cmdlet 上都指定它.

    附加信息

    这里有一些关于 Powershell 中 try/catch/finallyif 语句的官方文档。我没有涵盖上面的finally,但这是try/catch 语句的可选部分:

    关于如果https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_if?view=powershell-6

    关于 Try/Catch/Finallyhttps://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-6

    这里还有一些关于异常、错误以及在 Powershell 中处理它们的高级信息https://kevinmarquette.github.io/2017-04-10-Powershell-exceptions-everything-you-ever-wanted-to-know/

    【讨论】:

    • 衷心感谢您的深入回复。我怀疑有多种方法可以做到这一点!出于好奇,从开发的角度来看,最佳实践是什么?为什么?
    • 这主要取决于个人喜好。我的偏好是使用内联 if 语句并在那里检查我的 cmdlet 结果。在某些情况下,您希望使用 try/catch 立即出错并处理错误,而不是检查 if 语句中的结果并继续,但在这种情况下,您无需担心 race conditions,因为他们被称为。
    • 我添加了一些关于 iftry/catch/finally 和一般 Powershell 错误处理的附加信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-26
    • 2015-11-17
    • 1970-01-01
    • 1970-01-01
    • 2020-02-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多