我啥时候应该使用 Write-Error 与 Throw?终止与非终止错误

Posted

技术标签:

【中文标题】我啥时候应该使用 Write-Error 与 Throw?终止与非终止错误【英文标题】:When should I use Write-Error vs. Throw? Terminating vs. non-terminating errors我什么时候应该使用 Write-Error 与 Throw?终止与非终止错误 【发布时间】:2012-03-06 21:17:28 【问题描述】:

查看 PoshCode 上的 Get-WebFile 脚本http://poshcode.org/3226,我注意到了这个奇怪的装置:

$URL_Format_Error = [string]"..."
Write-Error $URL_Format_Error
return

与以下相反的原因是什么?

$URL_Format_Error = [string]"..."
Throw $URL_Format_Error

甚至更好:

$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error

据我了解,您应该将 Write-Error 用于非终止错误,将 Throw 用于终止错误,因此在我看来,您不应使用 Write-Error 后跟 Return。有区别吗?

【问题讨论】:

什么意思?如果 Write_error 允许脚本继续运行,那么在 Write-Error 之后有一个 return 语句是可以理解的。错误已被写出,您返回到最初调用该函数的代码。由于 Throw 用于终止错误,它会自动终止,因此 throw 声明中的 return 语句是无用的 @Gisli:重要的是要注意return 确实返回到(高级)函数的process 块中的调用者;相反,它继续到管道中的下一个输入对象。实际上,这是生成非终止错误的典型场景:如果仍然可以处理更多输入对象。 请注意,Throw 会生成一个 script 终止错误,这与触发的 statement 终止错误不同,例如,通过Get-Item -NoSuchParameter1 / 0 【参考方案1】:

Write-Error 如果您想通知用户非严重错误,则应使用。默认情况下,它所做的只是在控制台上以红色文本打印错误消息。它不会阻止管道或循环继续。另一方面,Throw 会产生所谓的终止错误。如果你使用 throw,管道和/或当前循环将被终止。事实上,除非您使用traptry/catch 结构来处理终止错误,否则所有执行都会终止。

有一点需要注意,如果您将 $ErrorActionPreference 设置为 "Stop" 并使用 Write-Error 它会产生终止错误

在您链接到的脚本中,我们发现:

if ($url.Contains("http")) 
       $request = [System.Net.HttpWebRequest]::Create($url)

else 
       $URL_Format_Error = [string]"Connection protocol not specified. Recommended action: Try again using protocol (for example 'http://" + $url + "') instead. Function aborting..."
       Write-Error $URL_Format_Error
    return
   

看起来该函数的作者想要停止执行该函数并在屏幕上显示一条错误消息,但不希望整个脚本停止执行。脚本作者本可以使用throw,但这意味着您在调用函数时必须使用try/catch

return 将退出当前作用域,该作用域可以是函数、脚本或脚本块。最好用代码来说明这一点:

# A foreach loop.
foreach ( $i in  (1..10) )  Write-Host $i ; if ($i -eq 5)  return  

# A for loop.
for ($i = 1; $i -le 10; $i++)  Write-Host $i ; if ($i -eq 5)  return  

两者的输出:

1
2
3
4
5

这里有个问题是使用returnForEach-Object。它不会像预期的那样中断处理。

更多信息:

$ErrorActionPreference: about_Preference_Variables try/catch: about_Try_Catch_Finally trap: about_Trap throw: about_Throw return: about_Return

【讨论】:

好的,所以 Throw 将停止一切,Write-Error + return 将仅停止当前函数。 @BillBarry 我用return的解释更新了我的答案。 Write-Error 后跟 exit(1) 以确保向操作系统返回适当的错误代码呢?这合适吗?【参考方案2】:

重要提示:有 2 种终止错误,不幸的是,当前的帮助主题混淆了

语句-终止错误,如某些不可恢复情况下的 cmdlet 以及发生 .NET 异常/PS 运行时错误的表达式所报告的;只有语句被终止,脚本默认继续执行

script-terminating 错误(更准确地说:runspace-terminating),由@987654329 触发@ 或通过common -ErrorAction parameter、-ErrorAction Stop 或通过$ErrorActionPreference preference variable、$ErrorActionPreference = 'Stop'升级其他错误类型之一。 除非被捕获,否则它们会终止当前的运行空间(线程);也就是说,它们不仅会终止当前脚本,还会终止所有调用者(如果适用)。

有关 PowerShell 错误处理的全面概述,请参阅GitHub documentation issue #1583。

本文的其余部分重点介绍 non-terminatingstatement-terminating 错误。


补充现有的有用答案,重点关注问题的核心:您如何选择报告声明-终止还是非终止错误

Cmdlet Error Reporting 包含有用的指南;让我尝试一个实用的总结

非终止错误背后的总体思路是允许对大型输入集进行“容错”处理:无法处理子集 的输入对象不应(默认情况下)中止 - 可能长时间运行的 - 进程作为一个整体,允许您检查错误并仅重新处理失败的 em> 对象稍后 - 通过自动变量$Error 中收集的错误记录报告。

报告NON-TERMINATING错误,如果您的 cmdlet/高级功能:

接受多个输入对象,通过管道输入和/或数组值参数,AND 特定输入对象发生错误,并且 这些错误原则上不会阻止对更多输入对象的处理在某种情况下,可能没有剩余输入对象和/或之前的输入对象可能已被成功处理)。 在高级函数中,使用$PSCmdlet.WriteError() 报告非终止错误(不幸的是,Write-Error 不会导致在 调用者的 范围内将$? 设置为$False - 见GitHub issue #3629)。 处理非终止错误:$? 告诉您最近的命令是否报告了至少一个非终止错误。 因此,$? 成为 $False 可能意味着任何(非空)输入对象子集未正确处理,可能是整个集合。 首选项变量$ErrorActionPreference 和/或通用cmdlet 参数-ErrorAction 可以(仅)在错误输出行为以及是否应将非终止错误升级到脚本-终止的。

所有其他情况中报告STATEMENT-TERMINATING错误。

值得注意的是,如果在仅接受 SINGLE 或 NO 输入对象并输出 NO 或 SINGLE 输出对象或采用 仅参数输入和参数的 cmdlet/高级函数中发生错误给定的值会阻止有意义的操作。 在高级函数中,您必须使用$PSCmdlet.ThrowTerminatingError() 才能生成语句终止错误。 请注意,相比之下,Throw 关键字会生成 脚本 终止错误,该错误会中止整个脚本(技术上:当前的线程 em>)。 处理语句终止错误:可以使用try/catch 处理程序或trap 语句(不能非终止一起使用错误),但请注意,即使是 statement-默认的终止错误也不会阻止脚本的其余部分运行。与非终止错误一样,$? 反映了$False,如果前一个语句触发了语句终止错误。

遗憾的是,并非所有 PowerShell 自己的核心 cmdlet 都遵守这些规则

虽然不太可能失败,但 New-TemporaryFile (PSv5+) 会在失败时报告非终止错误,尽管不接受管道输入并且只生成 一个 输出对象 - 这已得到更正至少 PowerShell [Core] 7.0,但是:请参阅 GitHub issue #4634。

Resume-Job 的帮助声称传递不受支持的作业类型(例如使用 Start-Job 创建的作业,这是不受支持的,因为 Resume-Job 仅适用于 workflow 作业)会导致一个终止错误,但从 PSv5.1 开始不是这样。

【讨论】:

【参考方案3】:

Write-Error cmdlet 和 PowerShell 中的throw 关键字之间的主要区别在于前者只是打印一些文本到standard error stream (stderr),而后者实际上是终止 正在运行的命令或函数的处理,然后由 PowerShell 通过向控制台发送有关错误的信息来处理。

您可以在您提供的示例中观察到两者的不同行为:

$URL_Format_Error = [string]"..."
Write-Error $URL_Format_Error
return

在此示例中,return 关键字已被添加到显式在错误消息发送到控制台后停止执行脚本。另一方面,在第二个示例中,return 关键字不是必需的,因为终止是由 throw 隐式完成的:

$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error

【讨论】:

如果你有 $ErrorActionPreference = "Stop",Write-Error 也会终止进程。 好消息,但 PowerShell 的错误流与其他 shell 中的 基于文本的 stderr 流类似,就像它包含的所有 PowerShell 流一样 objects,即[System.Management.Automation.ErrorRecord] 实例,默认收集在自动$Error 集合中($Error[0] 包含最近的错误)。即使您只是将Write-Error字符串 一起使用,该字符串也会被包装在[System.Management.Automation.ErrorRecord] 实例中。【参考方案4】:

除了Andy Arismendi's answer:

Write-Error 是否终止进程取决于$ErrorActionPreference 设置。

对于重要的脚本,$ErrorActionPreference = "Stop" 是 recommended setting 以快速失败。

"PowerShell 对错误的默认行为,即 continue on error ...感觉很 VB6 “On Error Resume Next”-ish”

(来自http://codebetter.com/jameskovacs/2010/02/25/the-exec-problem/)

但是,它使Write-Error 调用终止。

要将Write-Error 用作非终止命令而不考虑其他环境设置,您可以将common parameter -ErrorAction 与值Continue 一起使用:

 Write-Error "Error Message" -ErrorAction:Continue

【讨论】:

【参考方案5】:

Write-Error 允许函数的使用者使用-ErrorAction SilentlyContinue(或者-ea 0)抑制错误消息。而throw 需要try... catch ..

使用 try...catch 和 Write-Error

try 
    SomeFunction -ErrorAction Stop

catch 
    DoSomething

【讨论】:

如果你想抑制错误消息,为什么你需要调用 Write-Error ?【参考方案6】:

如果您对代码的阅读是正确的,那么您就是正确的。终止错误应使用throw,如果您正在处理 .NET 类型,那么遵循 .NET 异常约定也会很有帮助。

【讨论】:

以上是关于我啥时候应该使用 Write-Error 与 Throw?终止与非终止错误的主要内容,如果未能解决你的问题,请参考以下文章

我啥时候应该嘲笑?

我啥时候应该使用 FutureBuilder?

我啥时候应该使用 QThread::HighestPriority

Firebase:我啥时候应该使用 refreshToken?

我啥时候应该使用助手? [关闭]

我啥时候应该使用“while 循环”?