Попробуй поймай Powershell ... еще? - PullRequest
1 голос
/ 11 октября 2019

Я понимаю try-catch-finally в Powershell, но есть ли что-то похожее на предложение Python 'else', в котором код выполняется только при отсутствии ошибки?

Я пишу скрипт, которыйиспользует invoke-webRequest на сайте, который часто отключается. Я использую try-catch для отлова ошибок HTTP. Я также хочу, чтобы блок кода выполнялся, только если команда invoke-webRequest завершается успешно.

try{
    invoke-WebRequest -URI 'http://flakywebsite.com/site1' -Method GET
    invoke-WebRequest -URI 'http://flakywebsite.com/site2' -Method GET
    invoke-WebRequest -URI 'http://flakywebsite.com/site3' -Method GET
    }
catch{
    "This line will execute only if there is an error in the try block"
    }
else{
    "This line will execute only if there is NOT an error in the try block"
    }
finally{
    "This code will run regardless of whether there is or is not an error in the try block."
    }

У меня есть этот обходной путь. Он использует блок catch, чтобы добавить уведомление к переменной $ Error:

try{
    invoke-WebRequest -URI 'http://flakywebsite.com/site1' -Method GET
    invoke-WebRequest -URI 'http://flakywebsite.com/site2' -Method GET
    invoke-WebRequest -URI 'http://flakywebsite.com/site3' -Method GET
    }
catch{
    $Error.add("An error occurred in the try loop."}
    }
if($Error[-1] -ne "An error occurred in the try loop."){
    "This code will run only if there is NOT an error in the try block.
    }

Это работает, но ужасно уродливо. Есть ли лучший способ сделать это в Powershell?

Ответы [ 2 ]

3 голосов
/ 11 октября 2019

Нет, начиная с v7, PowerShell, к сожалению, не предлагает Python-подобное предложение else как часть оператора try / catch.


Один из вариантов - поместить код, пойманный только если не, внизувашего try блока , после команд, которые могут дать сбой - см. полезный ответ Зафера Балкана .

Если это нежелательно , я предлагаю следующееидиома как обходной путь :

# Make sure that even normally non-terminating errors trigger the
# `catch` block below.
# Note that the *first* error that occurs in the `try` block
# will jump to the `catch` block right away - subsequent commands won't execute.
$ErrorActionPreference = 'Stop'

# Save the count of errors currently recorded in the automatic $Errors collection.
$errCountBefore = $Error.Count

try {

    invoke-WebRequest -URI 'http://flakywebsite.com/site1' -Method GET
    invoke-WebRequest -URI 'http://flakywebsite.com/site2' -Method GET
    invoke-WebRequest -URI 'http://flakywebsite.com/site3' -Method GET

}
catch {
    # Write a custom error to the $Error collection, as the *first* 
    # (most recent) item.
    # Note: If you want the error to also *display*, use `-ErrorAction Continue`.
    Write-Error -ErrorAction SilentlyContinue "An error occurred in the try loop: $_"
}

if ($errCountBefore -eq $Error.Count) { # No errors occurred.
  "This code will run only if there is NOT an error in the try block."
}

Примечание: Если вы не записываете ошибку в свой блок catch, используйте пользовательскую переменную [bool], например $caught, который вы установили на $true в своем блоке catch, а затем проверьте на if (-not $caught).


Что касается того, что вы пытались :

$Error.add("An error occurred in the try loop."}

Вы не должны писать напрямую в $Error (что делает следующие пункты спорными: donдобавить строки ине добавлять к коллекции ошибок, которые она хранит) по следующим причинам:

$Error - это так называемая автоматическая переменная в PowerShell, что обычно означает, что PowerShell само управляет им , и пользовательский код не должен изменять его напрямую.

Единственное исключениеЧто касается $Error, то вы можете очистить набор ошибок, накопленных в сеансе на данный момент, используя $Error.Clear()

В противном случае PowerShell управляет $Error какследует:

  • Любая ошибка автоматически регистрируется в $Error, в обратном порядке в хронологическом порядке (самый последний - первый).

    • Единственными исключениями являются команды, которые вызываются с -ErrorAction Ignore и выводом stderr из внешних программ .
  • $Error содержит только error-объекты записи (экземпляры типа System.Management.Automation.ErrorRecord), которые предоставляют структурированную подробную информацию о each error.

Следовательно, весь ваш код, необходимый для записи ошибок в $Error, должен либо записать ошибку в поток ошибок с помощью Write-Error,или выдать ошибку завершения сценария с помощью оператора Throw - PowerShell обернет каждую ошибку в экземпляр System.Management.Automation.ErrorRecord, если необходимо, и добавит $Error в обратном хронологическом порядке.

1 голос
/ 11 октября 2019

Что ж, в вашем try-catch вы добавили много разных вещей. Лучшие практики рекомендуют один блок try для каждой из ваших команд Invoke-Request, поскольку вы не можете определить, какой из сайтов создал ошибку. Но вы можете просто добавить строку под вашим кодом.

try{
    invoke-WebRequest -URI 'http://flakywebsite.com/site1' -Method GET
    invoke-WebRequest -URI 'http://flakywebsite.com/site2' -Method GET
    invoke-WebRequest -URI 'http://flakywebsite.com/site3' -Method GET
    "This line will execute only if there is NOT an error in the try block"
    }
catch{
    "This line will execute only if there is an error in the try block"
    }
finally{
    "This code will run regardless of whether there is or is not an error in the try block."
    }
...