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


145

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


4
你什么意思?如果Write_error允许脚本继续运行,那么在Write-Error之后有一个return语句是很容易理解的。错误已被写出,您可以返回到首先调用该函数的代码。由于Throw用于终止错误,因此它将自动终止,因此throw声明中的return语句无用
Gisli 2012年

1
@Gisli:需要注意的是,在高级功能块return不会返回到调用者process;而是前进到管道中的下一个输入对象。确实,这是生成非终止错误的典型方案:如果仍然可以处理其他输入对象。
mklement0

1
请注意,这会Throw生成脚本终止错误,该错误与例如由或触发的语句终止错误不同。Get-Item -NoSuchParameter1 / 0
mklement0

Answers:


188

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在调用函数时必须使用a 。

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

这里的一个陷阱正在return与配合使用ForEach-Object。它不会像人们期望的那样中断处理。

更多信息:


好的,因此Throw将停止所有操作,Write-Error + return将仅停止当前功能。
比尔·巴里

@BillBarry我用的解释更新了我的答案return
安迪·阿里斯曼迪

那么如何在Write-Error之后加上exit(1)以确保将适当的错误代码返回给OS呢?那合适吗?
pabrams

19

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

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

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

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

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

2
如果您有$ ErrorActionPreference =“ Stop”,则Write-Error也将终止该过程。
Michael Freidgeim

4
好消息,但在PowerShell的错误流是类似基于文本的其他外壳标准错误流,像所有的PowerShell流它所包含的对象,即[System.Management.Automation.ErrorRecord]情况下,这是通过自动的收集默认$Error集合($Error[0]包含最新的错误)。即使你只使用Write-Error一个字符串,该字符串被包裹在一个[System.Management.Automation.ErrorRecord]实例。
mklement0

16

重要提示:有两种类型的终止错误,不幸的是,当前的帮助主题这些错误归为一类

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

  • 脚本终止错误(更准确地说:运行空间终止),是由Throw其他错误类型之一触发或通过错误操作偏好变量/参数值升级其他错误类型之一引起的Stop
    除非被捕获,否则它们将终止当前的运行空间(线程);否则,它们将终止。也就是说,它们不仅终止当前脚本,而且终止其所有调用方(如果适用)。

有关PowerShell错误处理的全面概述,请参阅此GitHub文档问题

这篇文章的其余部分集中在非终止语句终止的错误上。


为配合重点是问题的核心现有的有用的答案:你如何选择是否报告的陈述书终止不终止的错误

Cmdlet错误报告包含有用的指导;让我尝试一个实用的总结

背后的总体思路不终止,错误是允许“容错”的大输入集处理:故障处理一个子集输入对象的不应该(默认)中止的-可能长时间运行的-过程作为一个整体,通过自动变量中收集的错误记录,可以检查错误并稍后仅重新处理失败的对象$Error

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

    • 通过管道输入和/或数组值参数接受多个输入对象,并且
    • 特定输入对象发生错误,并且
    • 这些错误不能防止原则上对其他输入对象进行处理(在某些情况下,可能没有剩余的输入对象和/或以前的输入对象可能已经被成功处理)。
      • 在高级功能中,用于$PSCmdlet.WriteError()报告非终止错误(Write-Error不幸的是,不会导致在调用者的作用域内$?将其设置为-参见此GitHub问题)。$False
      • 处理非终止错误:$?告诉您最近的命令是否报告至少一个非终止错误。
        • 因此,$?存在$False可能意味着未正确处理输入对象的任何(非空)子集,可能未正确处理了整个集合。
        • 首选项变量$ErrorActionPreference和/或公共cmdlet参数-ErrorAction可以(仅)根据错误输出行为以及是否应将非终止错误升级为脚本终止错误来修改非终止错误的行为。
  • 其他所有情况下,请报告STATEMENT-TERMINATING错误。

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

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

  • 尽管不太可能失败,但New-TemporaryFile(PSv5 +)如果失败,将报告一个非终止错误,尽管它不接受管道输入并且仅产生一个输出对象-但至少从PowerShell [Core] 7.0起已得到纠正:但是,请参见此GitHub问题

  • Resume-Job的帮助声称,传递不受支持的作业类型(例如Start-Job,不支持通过创建的作业,因为Resume-Job仅适用于工作流作业,因此不受支持)会导致终止错误,但从PSv5.1开始并非如此。


8

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

要使用try ... catch与Write-Error

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}

如果要取消显示错误消息,为什么根本需要调用Write-Error?
Michael Freidgeim

8

除了Andy Arismendi的答案

Write-Error是否 终止处理取决于$ErrorActionPreference设置。

对于非平凡的剧本,$ErrorActionPreference = "Stop"是一个推荐设置快速失败。

“关于错误的PowerShell的默认行为,即继续发生错误...感觉非常VB6“错误恢复下一个” -ish”

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

但是,它使Write-Error呼叫终止。

要将Write-Error用作非终止命令,而不考虑其他环境设置,可以将普通参数 -ErrorAction与value一起使用Continue

 Write-Error "Error Message" -ErrorAction:Continue

0

如果您对代码的阅读是正确的,那么您是正确的。应该使用终止错误throw,并且如果您要处理.NET类型,则还应遵循.NET异常约定。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.