Как игнорировать конкретную ошибку в PowerShell при выполнении команды? - PullRequest
0 голосов
/ 06 февраля 2019

Я знаю ErrorAction аргумент, также $ErrorActionPreference, $Error и $.

Контекст

Проблема, которую я хотел бы решитькогда запускает внешнюю команду (например, choco или git), она выдает ошибку, которая должна быть предупреждением или даже не предупреждением, по крайней мере, в контексте моей задачи.

Из-за своего кода выхода PowerShell считает этот результат ошибкой в ​​каком-либо смысле, например, записывает красный на вывод и т. Д., Что нежелательно в контексте моей задачи.

Я могу подавить этивывод команд с ошибками -ErrorAction или 2> $null, но у меня плохое предчувствие полного исчезновения любых ошибок на этом пути конкретной команды.

Я хотел бы только игнорировать это известное "не проблема в этом контексте для меня "ошибка типа.

Вопрос

Есть ли способ обработать ошибку команды, игнорировать некоторые конкретные, но нормально обрабатывать все другие ошибкиусловия?

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019
  • В обычном консольном окне , в PowerShell v3 и выше строки stderr печатаются так же, как и строки stdout .

    • Это желательно, потому что stderr используется многими программами не только для сообщения о подлинных ошибках, но и для всего, что является не данными , например сообщениями о состоянии .

    • Строки Stderr (если не перенаправлены - см. Ниже) печатаются прямо через консоль (тогда как строки stdout отправляются в выходной поток успеха PowerShell, где их можно собирать в переменную, отправляя черезконвейер или перенаправлен с > / >>).

  • К сожалению, PowerShell ISE , даже вv5.1 , делает печать строк stderr в красный , в том же формате, что и ошибки PowerShell .

    • Код Visual Studio с расширением PowerShell не имеет этой проблемы, и его стоит перейти наВ общем, учитывая, что все будущие усилия по разработке будут сосредоточены там, и учитывая, что он также работает с кроссплатформенной версией PowerShell Core .
  • Хорошо функционирующие консольные приложения только используют код выхода , чтобы сигнализировать об успехе (код выхода 0) или об ошибке (любой ненулевой код выхода) .PowerShell сохраняет код завершения работы недавно запущенного консольного приложения в своей автоматической $LASTEXITCODE переменной .

  • В сторону:

    • Общие параметры -ErrorAction и -ErrorVariable нельзя использовать с внешними программами .
    • Аналогично, переменная предпочтения $ErrorActionPreference не действует (за исключением случайно , из-за эта ошибка , начиная с PowerShell v5.1 / PowerShell Core 6.2.0-preview.4).
    • try / catch нельзя использовать для обнаружения и обработки внешнегосбой программы.
    • Полный обзор правил обработки сложных ошибок PowerShell см. в этом выпуске документации по GitHub .

Примечание: IВ приведенных ниже примерах используются следующие внешние команды, которые производят 1 строку stdout и 1 строку вывода stderr (обратите внимание, что перенаправление >&2 обрабатывается cmd, а не PowerShell, поскольку оно находится внутри '...'; используется для получения вывода stderr):

cmd /c 'echo stdout & echo stderr >&2' 

Вариант, который делает командный сигнал сбой через ненулевой код выхода (exit 1):

cmd /c 'echo stdout & echo stderr >&2 & exit 1' 

Следовательно, обычно следующего должно хватить :

$stdOut = cmd /c 'echo stdout & echo stderr >&2' # std*err* will print to console
if ($LASTEXITCODE -ne 0) { Throw "cmd failed." } # handle error

Это захватывает вывод stdout в переменную $stdout и передает stderr вывод на консоль.


Если вы хотите собрать вывод stderr для позже и показывать их только в случае ошибка :

$stdErr = @() # initialize array for collecting stderr lines.

# Capture stdout output while collecting stderr output in $stdErr.
$stdOut = cmd /c 'echo stdout & echo stderr >&2 & exit 1' 2>&1  | Where-Object { 
  $fromStdErr = $_ -is [System.Management.Automation.ErrorRecord]
  if ($fromStdErr) { $stdErr += $_.ToString() }
  -not $fromStdErr # only output line if it came from stdout
}

# Throw an error, with the collected stderr lines included.
if ($LASTEXITCODE -ne 0) { 
  Throw "cmd failed with the following message(s): $stdErr"
}
  • Обратите внимание на перенаправление 2>&1, которое инструктирует PowerShell отправлять строки stderr (поток 2, поток ошибок) через конвейер (поток 1, поток успеха)), а также.

  • В блоке Where-Object $_ -is [System.Management.Automation.ErrorRecord] используется для идентификации строк stderr , потому что PowerShell оборачивает такие строки в экземплярах этого типа.

Кроме того, вы можете собрать вывод stderr во временный файл (2>/path/to/tmpfile), прочитать его содержимое и удалитьit.

В этом выпуске GitHub предлагается ввести возможность сбора перенаправленного вывода stderr в переменную , аналогично тому, как вы можете cmdlets собирать ошибки в переменной с помощью общего параметра
-ErrorVariable.

Существует два предостережения :

  • По сути, только печать stderrстроки позже , конкретный контекст относительно вывода stdout может быть потерян.

  • Из-за ошибки в Windows PowerShell v5.1 / PowerShell Core 6.2.0 , $ErrorActionPreference = Stop не должно быть эффект, потому что перенаправление 2>&1 затем вызывает ошибку завершения сценария , как только получена первая строка stderr.


Если вы хотите выборочно воздействовать на строки stderr по мере их получения :

Примечание:

  • По сути, как выобрабатывая строки, вы еще не будете знать, будет ли программа сообщать о неудаче или успехе в конце.

  • Здесь также применимо предостережение $ErrorActionPreference = 'Stop'.

Следующий пример фильтрует строку stderr stderr1 и печатает строку stderr2 на консоли красным цветом (только текст, не как ошибка PowerShell).

$stdOut = cmd /c 'echo stdout & echo stderr1 >&2 & echo stderr2 >&2' 2>&1 |
  ForEach-Object { 
    if ($_ -is [System.Management.Automation.ErrorRecord]) { # stderr line
      $stdErrLine = $_.ToString()
      switch -regex ($stdErrLine) {
        'stderr1' { break } # ignore, if line contains 'stderr1'
        default   { Write-Host -ForegroundColor Red $stdErrLine }
      }
    } else { # stdout line
      $_ # pass through
    }
  }

# Handle error.
if ($LASTEXITCODE -ne 0) { Throw "cmd failed." }
0 голосов
/ 06 февраля 2019

Я уже сталкивался с этой ситуацией с помощью git и обошел ее, используя $LASTEXITCODE.

Не уверен, применимо ли это к вашей ситуации или к choco, поскольку у меня не было проблем сит.

# using git in powershell console sends everything to error stream
# this causes red text when it's not an error which is annoying
# 2>&1 redirects all to stdout, then use exit code to 'direct' output

$git_output = Invoke-Expression "& [git pull etc] 2>&1"

# handle output using exitcode
if ($LASTEXITCODE -ne 0) { throw $git_output }
else { Write-Output $git_output }
...