我什么时候应该使用Write-Error vs. Throw?终止与非终止错误

时间:2012-02-15 14:13:36

标签: powershell error-handling powershell-v2.0

在PoshCode http://poshcode.org/3226上查看一个Get-WebFile脚本,我注意到这个奇怪的对我来说:

$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。有区别吗?

6 个答案:

答案 0 :(得分:157)

如果要通知用户非严重错误,则应使用

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一起使用。它不会像人们预期的那样破坏处理。

更多信息:

答案 1 :(得分:15)

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

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

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

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

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

答案 2 :(得分:10)

重要 2 类型的终止错误,当前的帮助主题很遗憾 conflate :< / p>

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

  • 脚本 - 终止错误,由Throw触发或通过错误操作首选项变量升级其他错误类型之一/参数值Stop
    除非被捕获,否则它们会终止当前执行的线程(即,不仅仅是当前脚本,还包括所有调用者,如果适用的话)。

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

本文的其余部分重点介绍非终止语句终止错误。

为了补充现有的有用答案,重点关注问题的核心:如何选择是否报告语句 - 终止非终止错误

Cmdlet Error Reporting包含有用的指南;让我尝试实用的摘要

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

  • 报告非终止错误,如果您的cmdlet /高级功能:

    • 通过管道输入和/或数组值参数接受MULTIPLE输入对象,AND
    • SPECIFIC输入对象,AND
    • 发生错误
    • 这些错误不会阻止处理另外的输入对象原理情境,可能没有输入对象和/或之前的输入对象可能已被处理成功)。
      • 在高级功能中,使用$PSCmdlet.WriteError()报告非终止错误(Write-Error,遗憾的是,$?不会导致$False设置为$? em>来电者的范围 - 见this GitHub issue)。
      • 处理非终止错误:$?告诉您最近的命令是否报告至少一个非终止错误。
        • 因此,$False$ErrorActionPreference可能意味着输入对象的任何(非空)子集未正确处理,可能是整个集合。
        • 首选变量-ErrorAction和/或公共cmdlet参数$PSCmdlet.ThrowTerminatingError()可以根据错误输出行为修改非终止错误的行为,以及是否应将非终止错误升级为< em> script -terminating ones。
  • 所有其他情况中报告声明终止错误。

    • 值得注意的是,如果在仅接受SINGLE或NO输入对象且输出NO或SINGLE输出对象的cmdlet /高级函数中发生错误。
      • 在高级功能中,必须使用Throw才能生成语句终止错误。
      • 请注意,相比之下,try/catch关键字会生成脚本 - 终止错误,该错误会中止整个脚本
      • 处理语句终止错误:可能会使用trap处理程序或$?语句(无法与一起使用非终止错误),但请注意,默认情况下,即使语句 - 终止错误也不会阻止脚本的其余部分运行。与非终止错误一样,如果前一个语句触发了语句终止错误,则$False会反映New-TemporaryFile

可悲的是,并非所有PowerShell自己的核心cmdlet都遵循这些规则

  • 虽然不太可能,Resume-Job(PSv5 +)会报告一个非终止错误,如果它失败了,尽管不接受管道输入并且只生成一个输出对象 - 这很可能但是,在v6中更改:请参阅this GitHub issue

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

答案 3 :(得分:7)

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

使用try {catch Write-Error

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}

答案 4 :(得分:5)

加入Andy Arismendi's answer

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

对于非平凡的脚本,$ErrorActionPreference = "Stop"recommended setting快速失败。

  

“PowerShell关于错误的默认行为,即   继续出错...感觉非常VB6“On Error Resume Next”-ish“

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

但是,它会使Write-Error个来电终止。

要将Write-Error用作非终止命令而不考虑其他环境设置,您可以使用值-ErrorAction的{​​{3}} Continue

 Write-Error "Error Message" -ErrorAction:Continue

答案 5 :(得分:0)

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