Как правильно выводить ошибки в функциях модуля powershell? - PullRequest
1 голос
/ 23 октября 2019

Я много читал об обработке ошибок powershell, и теперь я совершенно не понимаю, что мне делать в любой конкретной ситуации (обработка ошибок). Я работаю с PowerShell 5.1 (не ядро). С учетом сказанного: предположим, у меня есть модуль с функцией, которая будет выглядеть следующим образом:

function Set-ComputerTestConfig {
  [CmdletBinding()]
  param(
    [Parameter(Position=0, Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [string] $Name)

begin { ... }
process { 
 # task 1
 # task 2 => results in a failure that prevents further tasks
 # task 3
 # task 4
}
end { ... }

Предположим, что для каждого имени компьютера, которое я передаю этой функции, у меня есть 4 задачи для выполнения, ноесли какая-либо из задач не удалась, я не могу продолжить выполнение оставшихся задач. Как я должен выдавать ошибку (лучшую практику), которая останавливает «процесс» для этого конкретного имени компьютера, но эффективно продолжает обрабатывать конвейер?

Ответы [ 2 ]

1 голос
/ 23 октября 2019
  • Если вы хотите продолжить обработку входных данных из конвейера, вы должны выдать не прекращающуюся ошибку :

    • Write-Error записывает неразрывные ошибки;он записывает в поток ошибок PowerShell без генерации исключений;выполнение продолжается в обычном режиме.

      • Если вызов метода .NET является источником ошибки, как в вашем случае, оберните его в try / catch и вызовите Write-Error -ErrorRecord $_ вcatch блок:

        • try { <#task 1 #>; ... } catch { Write-Error -ErrorRecord $_ }
      • К сожалению, до сих пор в PowerShell Core 7.0.0-preview.4, Write-Errorне полностью ведет себя должным образом, поскольку не устанавливает для переменной автоматического статуса успеха, $?, значение $false в контексте вызывающего, как и должно быть. Единственный обходной путь в настоящее время состоит в том, чтобы убедиться, что ваша функция / скрипт является продвинутым и использовать $PSCmdlet.WriteError(); из catch блока вы можете просто использовать $PSCmdlet.WriteError($_), но создание собственной ошибки с нуля обременительно - см. этот выпуск GitHub .

  • Если вы хотите, чтобы остановился сразу , используйте завершающий ошибка :

    • throw создает завершающие ошибки.

      • К сожалению, throw создает более фундаментальный вид завершающей ошибки, чем двоичные командлетыemit: в отличие от ошибок, определяющих оператор , генерируемых (скомпилированными) командлетами, throw создает script , определяющих ошибку (ошибка завершения пространства выполнения)

        • То есть по умолчанию ошибка завершения оператора в двоичном командлете завершает только текущий оператор (конвейер) и продолжает выполнение включающего сценария, тогда как throw по умолчанию прерывает весь сценарий (и его вызывающих).
      • Опять же, обходной путь требует, чтобы ваш сценарий/ function - это advanced , которая позволяет вам вызывать $PSCmdlet.ThrowTerminatingError() вместо throw, который правильно генерирует оператор -определившую ошибку;как и в случае $PSCmdlet.WriteError(), вы можете просто использовать $PSCmdlet.ThrowTerminatingError($_) из catch блока , но создание собственной ошибки завершения оператора с нуля обременительно.

  • Как и для $ErrorActionPreference = 'Stop'

    • Это превращает все типы ошибок в script - определяющие ошибки и, по крайней мере, продвинутые функции / скрипты - те, которые должны работать как командлеты - должны не установить его .

    • Вместо этого сделайте так, чтобы ваш скрипт / функция выдавал соответствующие типы ошибок и позволял вызывающей стороне управлять ответом на них либо с помощью общего параметра -ErrorAction, либо с помощью $ErrorActionPreference variable.

      • Caveat : Функции в модулях do not см. Переменные предпочтения вызывающего, если вызывающий находится внемодуль или другой модуль - эта фундаментальная проблема обсуждается в этом выпуске GitHub .
  • Что касается передачи ошибок через / переупаковку их внутри скрипта функции :

    • Не прекращаетсяошибки автоматически пропускаются.

      • При необходимости вы можете подавить их с помощью -ErrorAction Ignore или 2>$null и при желании также собрать их для последующей обработки с помощью -ErrorVariable commonпараметр (в сочетании с -ErrorAction SilentlyContinue).
    • Скрипт ошибки, определяющие окончание, передаются в том смысле, что все пространство выполнения завершается вместе с вашимкод.

    • Оператор , определяющий ошибки, записывается в поток ошибок, но по умолчанию ваш скрипт / функция продолжает для выполнения.

      • Используйте try { ... } catch { throw } вместо того, чтобы превратить их в script -определительные ошибки, или ...

      • ... используйте $PSCmdlet.ThrowTerminatingError($_) вместо throw, чтобы передать ошибку как оператор , определяющий ошибку.

Дальнейшее чтение :

0 голосов
/ 23 октября 2019

Use Try ... Catch - убедитесь, что во всех командах используется переключатель -ErrorAction Stop или установите остановку среды при ошибке, например $ErrorActionPreference = 'Stop'

function Set-ComputerTestConfig {
  [CmdletBinding()]
  param(
    [Parameter(Position=0, Mandatory=$true)]
    [ValidateNotNullOrEmpty()]


    [string] $Name)

begin { ... }
process {
    Try {
        # task 1
        # task 2 => results in a failure that prevents further tasks
        # task 3
        # task 4
    }
    Catch {
        Write-Host "One of the tasks has failed`nError Message:"
        Write-Host $_
    }
}
end { ... }
...