PowerShell尝试/捕获并重试

Bra*_*rad 9 error-handling powershell try-catch

我有一个相当大的PowerShell脚本,有许多(20+)个函数执行各种操作.

现在所有的代码都没有任何错误处理或重试功能.如果某个特定的任务/功能失败,它就会失败并继续.

我想改进错误处理并实现重试以使其更加健壮.

我在想类似的东西:

$tries = 0
while ($tries -lt 5) {
    try{    

       # Do Something

       # No retries necessary
       $tries = 5;
    } catch {
       # Report the error
       # Other error handling
    }
 }
Run Code Online (Sandbox Code Playgroud)

问题是我需要做很多步骤才能做到这一点.

我认为实施上述代码20次是没有意义的.这似乎真的是多余的.

我在考虑编写一个带有单个参数的"TryCatch"函数,该参数包含我想要调用的实际函数?

不过,我不确定这是正确的方法.我不会最终得到一个类似于以下内容的脚本:

TryCatch "Function1 Parameter1 Parameter2"
TryCatch "Function2 Parameter1 Parameter2"
TryCatch "Function3 Parameter1 Parameter2"
Run Code Online (Sandbox Code Playgroud)

有一个更好的方法吗?

Ans*_*ers 16

如果您经常需要多次重试动作的代码,您可以将循环包装try..catch在函数中并在scriptblock中传递命令:

function Retry-Command {
    [CmdletBinding()]
    Param(
        [Parameter(Position=0, Mandatory=$true)]
        [scriptblock]$ScriptBlock,

        [Parameter(Position=1, Mandatory=$false)]
        [int]$Maximum = 5,

        [Parameter(Position=2, Mandatory=$false)]
        [int]$Delay = 100
    )

    Begin {
        $cnt = 0
    }

    Process {
        do {
            $cnt++
            try {
                $ScriptBlock.Invoke()
                return
            } catch {
                Write-Error $_.Exception.InnerException.Message -ErrorAction Continue
                Start-Sleep -Milliseconds $Delay
            }
        } while ($cnt -lt $Maximum)

        # Throw an error after $Maximum unsuccessful invocations. Doesn't need
        # a condition, since the function returns upon successful invocation.
        throw 'Execution failed.'
    }
}
Run Code Online (Sandbox Code Playgroud)

像这样调用函数(默认为5次重试):

Retry-Command -ScriptBlock {
    # do something
}
Run Code Online (Sandbox Code Playgroud)

或者像这样(如果在某些情况下需要不同的重试次数):

Retry-Command -ScriptBlock {
    # do something
} -Maximum 10
Run Code Online (Sandbox Code Playgroud)

该功能可以进一步改进,例如通过在$Maximum尝试失败后尝试使用另一个参数配置脚本终止,以便您可以拥有将导致脚本失败时停止的操作,以及可以忽略失败的操作.


Col*_*lin 8

我改编了@Victor 的回答并补充说:

  • 重试参数
  • ErrorAction 设置和恢复(否则不会捕获异常)
  • 指数退避延迟(我知道 OP 没有要求这个,但我使用它)
  • 摆脱了 VSCode 警告(即替换sleepStart-Sleep
# [Solution with passing a delegate into a function instead of script block](/sf/answers/3339896521/)
function Retry()
{
    param(
        [Parameter(Mandatory=$true)][Action]$action,
        [Parameter(Mandatory=$false)][int]$maxAttempts = 3
    )

    $attempts=1    
    $ErrorActionPreferenceToRestore = $ErrorActionPreference
    $ErrorActionPreference = "Stop"

    do
    {
        try
        {
            $action.Invoke();
            break;
        }
        catch [Exception]
        {
            Write-Host $_.Exception.Message
        }

        # exponential backoff delay
        $attempts++
        if ($attempts -le $maxAttempts) {
            $retryDelaySeconds = [math]::Pow(2, $attempts)
            $retryDelaySeconds = $retryDelaySeconds - 1  # Exponential Backoff Max == (2^n)-1
            Write-Host("Action failed. Waiting " + $retryDelaySeconds + " seconds before attempt " + $attempts + " of " + $maxAttempts + ".")
            Start-Sleep $retryDelaySeconds 
        }
        else {
            $ErrorActionPreference = $ErrorActionPreferenceToRestore
            Write-Error $_.Exception.Message
        }
    } while ($attempts -le $maxAttempts)
    $ErrorActionPreference = $ErrorActionPreferenceToRestore
}

# function MyFunction($inputArg)
# {
#     Throw $inputArg
# }

# #Example of a call:
# Retry({MyFunction "Oh no! It happened again!"})
# Retry {MyFunction "Oh no! It happened again!"} -maxAttempts 10
Run Code Online (Sandbox Code Playgroud)


小智 7

将委托传递给函数而不是脚本块的解决方案:

function Retry([Action]$action)
{
    $attempts=3    
    $sleepInSeconds=5
    do
    {
        try
        {
            $action.Invoke();
            break;
        }
        catch [Exception]
        {
            Write-Host $_.Exception.Message
        }            
        $attempts--
        if ($attempts -gt 0) { sleep $sleepInSeconds }
    } while ($attempts -gt 0)    
}

function MyFunction($inputArg)
{
    Throw $inputArg
}

#Example of a call:
Retry({MyFunction "Oh no! It happend again!"})
Run Code Online (Sandbox Code Playgroud)