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

在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错误。 默认情况下,它所做的只是在控制台上以红色文本输出错误消息。 它不会阻止pipe道或循环继续。 另一方面Throw产生所谓的终止错误。 如果使用throw,则stream水线和/或当前循环将被终止。 事实上,除非使用traptry/catch结构来处理终止错误,否则所有执行都将被终止。

有一点需要注意,如果将$ErrorActionPreference设置$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 = 0; $i -le 10; $i++) { Write-Host $i ; if ($i -eq 5) { return } } 

两者的输出:

 1 2 3 4 5 

这里的一个问题是使用ForEach-Object return 。 它不会像预期的那样破坏处理。

更多信息:

  • $ErrorActionPreference : http : //technet.microsoft.com/en-us/library/dd347731.aspx
  • try/catch : http : //technet.microsoft.com/en-us/library/dd315350.aspx
  • trap : http : //technet.microsoft.com/en-us/library/dd347548.aspx
  • throw : http : //technet.microsoft.com/en-us/library/dd819510.aspx
  • return : http : //technet.microsoft.com/en-us/library/dd347592.aspx

Write-Error cmdlet与PowerShell中的throw关键字之间的主要区别在于前者只是将一些文本打印到标准错误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 

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

使用Write-Error使用try … catch:

 try { SomeFunction -ErrorAction Stop } catch { DoSomething } 

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

  • 语句 –通过cmdlet在某些不可恢复的情况下以及由.NETexception/ PS运行时错误发生的expression式来报告错误。 只有语句被终止,并且脚本执行默认继续

  • 脚本 –终止错误,或者由Throw触发,或者通过error-action首选variables/参数值Stop升级其他错误types之一。

有关PowerShellerror handling的全面概述,请参阅此GitHub文档问题 。

本文的其余部分将重点讨论非终止语句终止错误。


为了补充现有的有帮助的答案,重点是问题的核心: 你如何select是否报告一个声明, 终止不终止错误

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

非终止错误背后的一般思想是允许对大型input集进行“容错”处理 :不应该(默认情况下)不处理input对象的一个​​子集作为整体终止可能长期运行的进程,以便稍后检查错误并仅重新处理失败的对象 – 如通过自动variables$Error收集的错误logging报告的那样。

  • 报告无终止错误,如果您的cmdlet /高级function:

    • 通过pipe道input和/或数组值参数AND 接受多个input对象
    • 对于特定的input对象 ,AND 发生错误
    • 这些错误不能阻止处理其他input对象IN PRINCIPLE (在情况下 ,可能没有input对象和/或以前的input对象可能已经被成功处理)。
      • 在高级函数中,使用$PSCmdlet.WriteError()来报告一个非终止错误( Write-Error ,不幸的是,在调用者的作用域中不会导致$?被设置为$False – 参见这个GitHub问题 )。
      • 处理一个非终止错误: $? 告诉您最近的命令是否报告至less一个非终止错误。
        • 因此, $?$False可能意味着input对象的任何(非空)子集都没有正确处理,可能是整个集合。
        • 首选variables$ErrorActionPreference和/或通用cmdlet参数-ErrorAction可以根据错误输出行为修改非终止错误的行为,以及非终止错误是否应升级为脚本终止错误。
  • 所有其他情况下报告一个STATEMENT-TERMINATING错误。

    • 值得注意的是, 如果在仅接受SINGLE或NOinput对象并输出NO或SINGLE输出对象的cmdlet / advanced函数中发生错误。
      • 在高级函数中,您必须使用$PSCmdlet.ThrowTerminatingError()才能生成语句终止错误。
      • 请注意,相比之下, Throw关键字会生成脚本终止错误,从而中止整个脚本
      • 处理语句终止错误:可以使用try/catch处理程序或trap语句( 不能非终止错误一起使用),但是请注意,即使是默认的声明 –终止错误也不会阻止脚本的其他部分运行。 与非终止错误一样, $? 如果前面的语句触发了语句终止错误,则会反映$False

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

  • 虽然不太可能,但是New-TemporaryFile (PSv5 +)会报告一个非终止错误,尽pipe它不接受pipe道input并且只产生一个输出对象 – 但是这可能会在v6中改变,但是:看到这个GitHub问题 。

  • Resume-Job的帮助声称,通过不受支持的作业types(例如,使用Start-Job创buildStart-Job不受支持,因为Resume-Job仅适用于工作stream作业)会导致终止错误,但这不是PSv5.1。

除了Andy Arismendi的回答 :

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

对于非平凡脚本, $ErrorActionPreference = "Stop"是推荐的设置,可以快速失败。

“PowerShell的错误的默认行为,这是继续错误…感觉非常VB6”错误恢复下一步“-ish”

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

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

要将Write-Error用作非终止命令而不pipe其他环境设置如何,可以使用通用参数 -ErrorAction ,其值为Continue

  Write-Error "Error Message" -ErrorAction:Continue 

如果你阅读的代码是正确的,那么你是正确的。 终止错误应该使用throw ,如果你正在处理.NETtypes,那么也遵循.NETexception约定是有帮助的。