首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >PowerShell尝试/捕获和重试

PowerShell尝试/捕获和重试
EN

Stack Overflow用户
提问于 2017-08-02 21:06:34
回答 4查看 37.8K关注 0票数 26

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

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

我想改进错误处理和实现重试,以使它更加健壮。

我在想一些类似的事情:

代码语言:javascript
复制
$tries = 0
while ($tries -lt 5) {
    try{    

       # Do Something

       # No retries necessary
       $tries = 5;
    } catch {
       # Report the error
       # Other error handling
    }
 }

问题是,我有许多步骤需要这样做。

我不认为将上述代码实现20次是有意义的。这似乎是多余的。

我正在考虑用一个包含我想要调用的实际函数的参数来编写一个"TryCatch“函数。

不过,我也不确定这是不是正确的方法。我会不会得到一个脚本,读起来像:

代码语言:javascript
复制
TryCatch "Function1 Parameter1 Parameter2"
TryCatch "Function2 Parameter1 Parameter2"
TryCatch "Function3 Parameter1 Parameter2"

有更好的方法吗?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2017-08-02 23:06:04

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

代码语言:javascript
复制
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 {
                # If you want messages from the ScriptBlock
                # Invoke-Command -Command $ScriptBlock
                # Otherwise use this command which won't display underlying script messages
                $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.'
    }
}

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

代码语言:javascript
复制
Retry-Command -ScriptBlock {
    # do something
}

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

代码语言:javascript
复制
Retry-Command -ScriptBlock {
    # do something
} -Maximum 10

该函数还可以进一步改进,例如,在$Maximum失败尝试之后使用另一个参数进行可配置的脚本终止,这样您就可以拥有将导致脚本在失败时停止的操作,以及可以忽略其失败的操作。

票数 50
EN

Stack Overflow用户

发布于 2019-08-14 23:30:41

我改写了维克多的回答,并补充道:

  • 参数用于重试
  • ErrorAction设置和恢复(否则异常不会被捕获)
  • 指数退避延迟(我知道OP没有要求这个,但我使用它)
  • 消除VSCode警告(即将sleep替换为Start-Sleep)
代码语言:javascript
复制
# [Solution with passing a delegate into a function instead of script block](https://stackoverflow.com/a/47712807/)
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
票数 10
EN

Stack Overflow用户

发布于 2017-12-08 10:50:06

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

代码语言:javascript
复制
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!"})
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45470999

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档