如何在没有附带堆栈跟踪的情况下在 PowerShell 中显示 'naked' 错误消息?
How can I display a 'naked' error message in PowerShell without an accompanying stacktrace?
如何从 PowerShell 写入标准错误,或捕获以下错误:
- 错误信息显示为错误(真正写入标准错误,以便TeamCity和Octopus将其视为错误)
- 没有堆栈跟踪垃圾混淆了我漂亮、简洁的错误消息
这些年来,我一直在 throw
错误或通过 Write-Error
编写,但我又累又老,在我的脚本中我只想看到一条简明的错误消息.我一直在尝试 trap
、throw
、Write-Error
和 -ErrorAction
的所有组合,但都无济于事:
try {
throw "error" # Sample code for a stack overflow. 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:\> ▏
将自动 $ErrorView
变量设置为 'CategoryView'
会导致 PowerShell 输出简洁的 单行 错误表示,但这表示可能并不总是包含 足够 信息,因为错误 message 通常 not 包含在内;从好的方面来说,传递给 Throw "..."
的文本是 反映出来的,但是相比之下,Write-Error
输出包含 no 'CategoryView'
期间的具体信息。
向单行但始终包含所有关键信息的 PowerShell 添加新的错误视图是 being discussed for v6.
假设您的 PowerShell 代码是 运行 来自 console(使用控制台主机),使用[Console]::Error.WriteLine()
,无条件写入外部世界的 stderr(标准错误流):
[Console]::Error.WriteLine("An error occurred ... Have you tried rebooting?")
注:
这不适用于非控制台主机,例如 PowerShell ISE。
[Console]::Error.WriteLine()
输出未在控制台 red 中打印 [1].
可悲的是,没有一种解决方案既适用于在 PowerShell(跨主机)和来自在之外:
[Console]::Error.WriteLine()
,在为外部世界正确写入 stderr 时,无法捕获或抑制其输出内部 PowerShell,并且仅适用于 PowerShell console 主机。
类似地,$host.ui.WriteErrorLine()
,即使适用于 所有 主机,它也是 UI 方法也在 PowerShell 的流系统之外工作,因此其输出也无法在 PowerShell 中捕获或抑制。
更重要的是,它不会写入外部世界的 stderr(在这方面它的行为类似于 Write-Error
,见下文)。
在 PowerShell中,只有Write-Error
写入PowerShell的错误流,所以它的输出可以是捕获/压制。
然而,不幸的是,Write-Error
(除了吵之外)确实不写入外界的stderr,unless,奇怪的是,stderr是明确被重定向 - 详情请参阅我的 this answer。
[1] Peter(OP 本人)为此提供了一个解决方法:
[Console]::ForegroundColor = 'red'
[Console]::Error.WriteLine("An error occurred ... Have you tried rebooting?")
[Console]::ResetColor()
为它提供了一个函数包装器。
幸运的是,PowerShell 在检测到输出被重定向(到文件)时会自动忽略颜色代码。
我认为在 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 块来处理代码中的多个错误。您还可以包含一个 "blanket" 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!"
}
基于 ,您可以使用自定义函数临时覆盖内置的 Write-Error cmdlet。
# 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 函数优先于 cmdlet 的事实。
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)
基于 我编写了以下函数,让您可以毫不费力地将 Write-Error 与自定义函数交换并返回。我还添加了检查用户是否从 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"
如何从 PowerShell 写入标准错误,或捕获以下错误:
- 错误信息显示为错误(真正写入标准错误,以便TeamCity和Octopus将其视为错误)
- 没有堆栈跟踪垃圾混淆了我漂亮、简洁的错误消息
这些年来,我一直在 throw
错误或通过 Write-Error
编写,但我又累又老,在我的脚本中我只想看到一条简明的错误消息.我一直在尝试 trap
、throw
、Write-Error
和 -ErrorAction
的所有组合,但都无济于事:
try {
throw "error" # Sample code for a stack overflow. 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:\> ▏
将自动 $ErrorView
变量设置为 'CategoryView'
会导致 PowerShell 输出简洁的 单行 错误表示,但这表示可能并不总是包含 足够 信息,因为错误 message 通常 not 包含在内;从好的方面来说,传递给 Throw "..."
的文本是 反映出来的,但是相比之下,Write-Error
输出包含 no 'CategoryView'
期间的具体信息。
向单行但始终包含所有关键信息的 PowerShell 添加新的错误视图是 being discussed for v6.
假设您的 PowerShell 代码是 运行 来自 console(使用控制台主机),使用[Console]::Error.WriteLine()
,无条件写入外部世界的 stderr(标准错误流):
[Console]::Error.WriteLine("An error occurred ... Have you tried rebooting?")
注:
这不适用于非控制台主机,例如 PowerShell ISE。
[Console]::Error.WriteLine()
输出未在控制台 red 中打印 [1].
可悲的是,没有一种解决方案既适用于在 PowerShell(跨主机)和来自在之外:
[Console]::Error.WriteLine()
,在为外部世界正确写入 stderr 时,无法捕获或抑制其输出内部 PowerShell,并且仅适用于 PowerShell console 主机。类似地,
$host.ui.WriteErrorLine()
,即使适用于 所有 主机,它也是 UI 方法也在 PowerShell 的流系统之外工作,因此其输出也无法在 PowerShell 中捕获或抑制。
更重要的是,它不会写入外部世界的 stderr(在这方面它的行为类似于Write-Error
,见下文)。在 PowerShell中,只有
Write-Error
写入PowerShell的错误流,所以它的输出可以是捕获/压制。
然而,不幸的是,Write-Error
(除了吵之外)确实不写入外界的stderr,unless,奇怪的是,stderr是明确被重定向 - 详情请参阅我的 this answer。
[1] Peter(OP 本人)为此提供了一个解决方法:
[Console]::ForegroundColor = 'red'
[Console]::Error.WriteLine("An error occurred ... Have you tried rebooting?")
[Console]::ResetColor()
幸运的是,PowerShell 在检测到输出被重定向(到文件)时会自动忽略颜色代码。
我认为在 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 块来处理代码中的多个错误。您还可以包含一个 "blanket" 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!"
}
基于
# 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 函数优先于 cmdlet 的事实。
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)
基于
# 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"