如何在没有随附堆栈跟踪的情况下在 PowerShell 中显示"裸"错误消息?



如何从PowerShell写入标准错误,或捕获错误,以便:

错误
  • 信息显示为错误(真正写入标准错误,以便TeamCity和八达通将其视为错误)
  • 没有堆栈跟踪垃圾混淆了我美丽,简洁的错误消息

这些年来,我一直通过throw错误或通过Write-Error写作而幸存下来,但我又累又老,在我的脚本中,我只想看到一条简洁的错误消息。我一直在尝试trapthrowWrite-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首选项变量$ErrorView变量设置为'CategoryView'会导致 PowerShell 改为输出简洁的单行错误表示形式,但此表示形式可能并不总是包含足够的信息,因为通常不包括错误消息;从好的方面来说,传递给Throw "..."的文本得到反映,但相比之下,Write-Error输出在'CategoryView'生效时不包含任何特定信息。

  • 虽然PowerShell(核心)7+现在默认通过其新的$ErrorView默认值ConciseView简洁的错误格式(在最简单的情况下打印错误消息),但此输出仅在上下文中是单行的<。/>值得注意的是,如果错误发生在脚本(.*ps1文件)或来自脚本的点源function,它仍然是多行的, (包括配置文件脚本),在这种情况下,错误消息前面会附加报告脚本的文件路径、源代码行和行号的行。


假设您的 PowerShell 代码是从控制台(使用控制台主机)运行的,请使用[Console]::Error.WriteLine(),它无条件地写入外部世界的stderr(标准错误流):

[Console]::Error.WriteLine("An error occurred ... Have you tried rebooting?")

注意:

  • 这不适用于非控制台主机,如PowerShell ISE。

  • [Console]::Error.WriteLine()输出不会在控制台[1]中以红色打印。


遗憾的是,没有一个解决方案既可以在PowerShell内部(跨主机)工作,也可以在PowerShell外部工作

  • [Console]::Error.WriteLine()虽然可以正确写入外部世界的stderr,但无法在 PowerShell捕获或抑制其输出,并且仅适用于 PowerShell控制台主机。

  • 同样,$host.ui.WriteErrorLine(),即使适用于所有主机,它也是一种在PowerShell的流系统之外工作的UI方法,因此它的输出也无法在PowerShell.
    中捕获或抑制,更重要的是,它不会写入外部世界的stderr(它在这方面的行为类似于Write-Error,见下文)。

  • PowerShell内部,只有Write-Error写入PowerShell的错误流,因此可以捕获/抑制其输出.
    然而,不幸的是,Write-Error(除了嘈杂)不会写入外部世界的stderr,除非奇怪的是,stderr被明确重定向- 有关详细信息,请参阅我的答案。


[1] Peter(OP本人)为此提供了一个解决方法:

[Console]::ForegroundColor = 'red'
[Console]::Error.WriteLine("An error occurred ... Have you tried rebooting?")
[Console]::ResetColor()

Suneg的有用答案为它提供了一个函数包装器。

幸运的是,PowerShell 在检测到输出正在重定向(到文件)时会自动省略颜色代码。

基于上一个答案中的想法,可以使用自定义函数临时替代内置的写入错误 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 Functions优先于cmdlet的事实。

https://technet.microsoft.com/en-us/library/hh848304.aspx

这是我能够想到的最优雅的方法,既可以显示漂亮简洁的错误消息,又可以让 TeamCity 轻松检测问题。

我最近需要自己解决这个问题,所以我把一个写错误消息函数放在一起,详见这里: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 块来处理代码中的多个错误。您还可以包含一个"一揽子"捕获块来捕获您尚未处理的所有错误。例:

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!"
}

根据 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"

Powershell 7提供了新的错误视图类别"简洁视图",该类别应抑制"噪音"。

Powershell:

$ErrorView = 'ConciseView'
Get-ChildItem -path 'C:NoRealDirectory'

输出:

Get-ChildItem: Cannot find path 'C:NoRealDirectory' because it does not exist.

PowerShell-7.2#错误视图

相关内容

  • 没有找到相关文章

最新更新