Как я могу отобразить "пустое" сообщение об ошибке в PowerShell без сопровождающей трассировки стека?
Как я могу написать в stderr из PowerShell, или перехватить ошибки, которые
- Сообщение об ошибке отображается как ошибка (действительно записывается в stderr, чтобы TeamCity и Octopus воспринимали его как ошибку)
- Отсутствие мусора в стеке мешает мое красивое, лаконичное сообщение об ошибке
Все эти годы я пережил throw
ошибки или запись через Write-Error
, но я устал и стар, и в моих сценариях я просто хочу увидеть одно краткое сообщение об ошибке. Я пробовал каждую комбинацию trap
, throw
, Write-Error
, а также -ErrorAction
, но безрезультатно:
try {
throw "error" #sample code for Stackru. In the theater
#of your mind, imagine there is code here that does something real and useful
} catch {
Write-Error "An error occurred attempting to 'do something.' Have you tried rebooting?"
}
Вот пользовательский опыт, который я хочу увидеть:
C:\> & .\Do-Something.ps1
An error occurred attempting to 'do something.' Have you tried rebooting?
C:\> ▏
Вместо этого я получаю:
C:\> & .\Do-Something.ps1
An error occurred attempting to 'do something.' Have you tried rebooting?
At line:1 char:1
+ Do-RealWork
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Do-RealWork
C:\> ▏
6 ответов
Настройка автоматического $ErrorView
переменная к 'CategoryView'
заставляет PowerShell выводить краткие однострочные сообщения об ошибках, но это представление не всегда может включать в себя достаточно информации, поскольку сообщение об ошибке обычно не включается; с другой стороны, текст передан Throw "..."
отражается, но, напротив, Write-Error
вывод не содержит никакой конкретной информации, в то время как 'CategoryView'
в силе.
Для версии 6 обсуждается добавление нового представления ошибок в PowerShell, которое является однострочным, но всегда содержит всю важную информацию.
При условии, что ваш код PowerShell запускается из консоли (использует хост консоли), используйте [Console]::Error.WriteLine()
который безоговорочно записывает в stderr внешнего мира (стандартный поток ошибок):
[Console]::Error.WriteLine("An error occurred ... Have you tried rebooting?")
Замечания:
Это не будет работать с не консольными хостами, такими как PowerShell ISE.
[Console]::Error.WriteLine()
вывод не печатается красным в консоли [1].
К сожалению, нет единого решения, которое бы работало как внутри PowerShell (на разных хостах), так и вне его:
[Console]::Error.WriteLine()
при правильной записи в stderr для внешнего мира его выходные данные не могут быть записаны или подавлены внутри PowerShell и работают только с консольным хостом PowerShell.Так же,
$host.ui.WriteErrorLine()
Несмотря на то, что он работает со всеми хостами, это метод пользовательского интерфейса, который работает и за пределами потоковой системы PowerShell, и, следовательно, его вывод также не может быть захвачен или подавлен в PowerShell.
Что еще более важно, он не пишет в stderr внешнего мира (он ведет себя какWrite-Error
в этом отношении см. ниже).Только внутри PowerShell
Write-Error
пишет в поток ошибок PowerShell, поэтому его вывод может быть захвачен / подавлен.
Однако, к сожалению,Write-Error
(кроме шума) не пишет в stderr внешнего мира, если только странным образом не перенаправляется stderr - подробности смотрите в моем ответе.
[1] Питер (сам ОП) предлагает обходной путь для этого:
[Console]::ForegroundColor = 'red'
[Console]::Error.WriteLine("An error occurred ... Have you tried rebooting?")
[Console]::ResetColor()
Полезный ответ suneg предоставляет функциональную оболочку для него.
К счастью, PowerShell автоматически пропускает цветовые коды, когда обнаруживает, что вывод перенаправляется (в файл).
Основываясь на идее предыдущего ответа, вы можете временно переопределить встроенный командлет Write-Error с помощью пользовательской функции.
# Override the built-in cmdlet with a custom version
function Write-Error($message) {
[Console]::ForegroundColor = 'red'
[Console]::Error.WriteLine($message)
[Console]::ResetColor()
}
# Pretty-print "Something is wrong" on stderr (in red).
Write-Error "Something is wrong"
# Setting things back to normal
Remove-Item function:Write-Error
# Print the standard bloated Powershell errors
Write-Error "Back to normal errors"
При этом вы используете тот факт, что функции Powershell имеют приоритет над командлетами.
https://technet.microsoft.com/en-us/library/hh848304.aspx
Это самый элегантный подход, который я смог придумать, чтобы показывать красивые и краткие сообщения об ошибках, а также позволял TeamCity легко обнаруживать проблемы.
Недавно мне нужно было решить эту проблему самостоятельно, поэтому я собрал функцию Write-ErrorMessage, как описано здесь: https://intellitect.com/powershell-write-error-without-writing-stack-trace/
В частности, я использовал комбинацию
Write - Error - Message $err - ErrorAction SilentlyContinue
$Host.UI.WriteErrorLine($errorMessage)
На мой взгляд, лучший способ отловить ошибки в PowerShell - использовать следующее:
$Error[0].Exception.GetType().FullName
Вот пример того, как использовать это правильно. В основном тестируйте то, что вы пытаетесь сделать в PowerShell, с различными сценариями, в которых ваш скрипт потерпит неудачу.
Вот типичное сообщение об ошибке PowerShell:
PS C:\> Stop-Process -Name 'FakeProcess'
Stop-Process : Cannot find a process with the name "FakeProcess". Verify the process name and call the cmdlet again.
At line:1 char:1
+ Stop-Process -Name 'FakeProcess'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (FakeProcess:String) [Stop-Process], ProcessCommandException
+ FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.StopProcessCommand
Далее вы получите исключение из сообщения об ошибке:
PS C:\> $Error[0].Exception.GetType().FullName
Microsoft.PowerShell.Commands.ProcessCommandException
Вы должны настроить свой код, чтобы перехватить сообщение об ошибке следующим образом:
Try
{
#-ErrorAction Stop is needed to go to catch statement on error
Get-Process -Name 'FakeProcess' -ErrorAction Stop
}
Catch [Microsoft.PowerShell.Commands.ProcessCommandException]
{
Write-Host "ERROR: Process Does Not Exist. Please Check Process Name"
}
Вывод будет выглядеть следующим образом вместо стандартной ошибки Powershell в приведенном выше примере:
ERROR: Process Does Not Exist. Please Check Process Name
Наконец, вы также можете использовать несколько блоков catch для обработки нескольких ошибок в вашем коде. Вы также можете включить "общий" блок catch, чтобы перехватывать все ошибки, которые вы не обработали. Пример:
Try
{
Get-Process -Name 'FakeProcess' -ErrorAction Stop
}
Catch [Microsoft.PowerShell.Commands.ProcessCommandException]
{
Write-Host "ERROR: Process Does Not Exist. Please Check Process Name"
}
Catch [System.Exception]
{
Write-Host "ERROR: Some Error Message Here!"
}
Catch
{
Write-Host "ERROR: I am a blanket catch to handle all unspecified errors you aren't handling yet!"
}
Надеюсь это поможет!
Powershell 7 предоставляет новую категорию просмотра ошибок «ConciseView», которая должна подавлять «шумы».
PowerShell:
$ErrorView = 'ConciseView'
Get-ChildItem -path 'C:\NoRealDirectory'
Выход:
Get-ChildItem: Cannot find path 'C:\NoRealDirectory' because it does not exist.
Основываясь на ответе suneg, я написал следующие функции, которые позволят вам легко заменить ошибку записи на пользовательскую функцию и обратно. Я также добавил проверку, вызывает ли пользователь ошибку записи из PowerShell ISE.
# Override the built-in cmdlet with a custom version
function New-ErrorFunc {
function Dyn($message){
param($message,$ErrorAction)
if($psISE){
$Host.UI.WriteErrorLine($message)
}
else{
[Console]::ForegroundColor = 'red'
[Console]::Error.WriteLine($message)
[Console]::ResetColor()
}
if($ErrorAction -eq 'Stop'){
Break
}
}
return ${function:Dyn}
}
function Set-ErrorFunc(){
param([bool]$custom=$true)
if($custom){
$dynfex= New-ErrorFunc
Invoke-Expression -Command "function script:Write-Error{ $dynfex }"
}
else {
$custom= Get-Command Write-Error | Where-Object {$_.CommandType -eq 'Function'}
if($custom){ Remove-Item function:Write-Error }
}
}
#User our Custom Error Function
Set-ErrorFunc
# Pretty-print "Something is wrong" on stderr (in red).
Write-Error "Something is wrong"
# Setting things back to normal
Set-ErrorFunc -custom $false
# Print the standard bloated Powershell errors
Write-Error "Back to normal errors"