如何在 powershell 中不断尝试直到成功?

How to continuously try something in powershell until it succeeds?

使用基本构造:

try
{
    Do-Something
}
catch
{
    Write-Output "Something threw an exception"
}

是否可以一直尝试 Do-Something 直到成功?也许像这样使用 while loopp:

$Timeout = 60
$timer = [Diagnostics.Stopwatch]::StartNew()
$t = 0
while (($timer.Elapsed.TotalSeconds -lt $Timeout) -and ($t -ne 1))) {
    Start-Sleep -Seconds 1
    try
    {
        Do-Something
        $t = 1
    }
    catch
    {
        Write-Output "Something threw an exception"
    }
}
$timer.Stop()

在这里,我使用计时器来确保 PowerShell 不会无限期地 运行。 它应该继续尝试,直到 try 成功并且 $t = 1 被执行。但是,它会在大约 2 秒内失败。请帮忙。

更具体地说,Do-Something 是:

(Get-Process -Name FineReader).MainWindowHandle

我希望代码继续尝试直到 FineReader 存在并且它可以获得 MainWindowHandle.

您的 Do-Something 应该使用开关 -ErrorAction Stop 调用,以便发出可以被 try

捕获的终止异常

为此,您还需要将函数绑定为 CmdLet。例如:

function DoSomething {
    [CmdLetBinding()]
    Param(
    )

    # Your code

}

然后使用 -ErrorAction Stop 开关调用您的函数:

try {
    Do-Something -ErrorAction Stop
}

如果您的 DoSomething 不是函数而是现有的 powershell CmdLet,那么...您猜对了,只需使用 -ErrorAction Stop

调用它

您可以在 powershell here

中了解有关 try/catch/finally 的更多信息

您可以使用 break 关键字。

# Set the erroracton preference to stop when an error occurs,
$ErrorActionPreferenceBak = $ErrorActionPreference
$ErrorActionPreference    = 'Stop'

While($True){
    try{
        Do-Something
        break
    }
    catch{
        Write-Output "Something failed"
        Start-Sleep -Seconds 1 # wait for a seconds before next attempt.
    }
    finally{
        #Reset the erroracton preference
        $ErrorActionPreference = $ErrorActionPreferenceBak
    }
}

假设您有一个函数在 10 次调用中有 9 次失败:

function Get-RandomFail{
    $value = [Random]::new().Next(10);
    if ($value -ne 5) { throw }
    return $value
}

而你想限制时间window,你可以使用以下方法:

function Try-Invoke{
    [CmdletBinding()]
    param(
        $Action,
        $MaxSeconds,
        $Delay=1
    )
    $timeout = [timespan]::FromSeconds($MaxSeconds)
    $start = [DateTime]::Now
    do {
        try {
            return &$Action
        } catch {
            Write-Verbose "Error"
        }
        Start-Sleep -Seconds $Delay
    } until (([DateTime]::Now - $start) -gt $timeout)
    throw
}

$result = Try-Invoke {Get-RandomFail} -MaxSeconds 5 -Verbose

Get-RandomFail 将被调用,直到没有错误发生或直到时间结束。您还可以使用 Delay 参数在每次不成功的 Get-RandomFail 调用后修改睡眠时间。