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

Bil*_*rry 134 error-handling powershell powershell-2.0

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

$URL_Format_Error = [string]"..."
Write-Error $URL_Format_Error
return
Run Code Online (Sandbox Code Playgroud)

与以下相反,这是什么原因?

$URL_Format_Error = [string]"..."
Throw $URL_Format_Error
Run Code Online (Sandbox Code Playgroud)

甚至更好:

$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error
Run Code Online (Sandbox Code Playgroud)

据我所知,你应该使用Write-Error来解决非终止错误,并使用Throw来终止错误,所以在我看来你不应该使用Write-Error然后使用Return.有区别吗?

And*_*ndi 168

Write-Error如果要通知用户非严重错误,应使用此选项.默认情况下,它只是在控制台上以红色文本打印错误消息.它不会阻止管道或循环继续.Throw另一方面,产生所谓的终止错误.如果使用throw,则管道和/或电流循环将终止.事实上,除非您使用一个trap或一个try/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
   }
Run Code Online (Sandbox Code Playgroud)

看起来该函数的作者想要停止执行该函数并在屏幕上显示错误消息但不希望整个脚本停止执行.脚本作者可能已经使用过,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 } }
Run Code Online (Sandbox Code Playgroud)

两者的输出:

1
2
3
4
5
Run Code Online (Sandbox Code Playgroud)

有一个问题在这里使用returnForEach-Object.它不会像人们预期的那样破坏处理.

更多信息:

  • Write-Error 后跟 exit(1) 怎么样,以确保将适当的错误代码返回到操作系统?这合适吗? (3认同)

Enr*_*lio 17

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

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

$URL_Format_Error = [string]"..."
Write-Error $URL_Format_Error
return
Run Code Online (Sandbox Code Playgroud)

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

$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error
Run Code Online (Sandbox Code Playgroud)

  • 好的信息,但PowerShell的错误流是_analogous_到其他shell中的_text-based_ stderr流,就像所有PowerShell流一样,它包含_objects_,即`[System.Management.Automation.ErrorRecord]`实例,默认情况下收集在自动`$ Error`集合(`$ Error [0]`包含最新的错误).即使您只使用带有_string_的`Write-Error`,该字符串也会被包含在`[System.Management.Automation.ErrorRecord]`实例中. (4认同)
  • 如果你有$ ErrorActionPreference ="Stop",Write-Error也会终止进程. (2认同)

mkl*_*nt0 13

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

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

  • script -terminating errors,由Throw其他错误类型之一触发或通过error-action preference-variable/parameter值升级Stop.
    除非被捕获,否则它们会终止当前执行的线程(即,不仅仅是当前脚本,还包括其所有调用者,如果适用).

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

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


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

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

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

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

    • 通过管道输入和/或数组值参数AND 接受MULTIPLE输入对象
    • 对于SPECIFIC输入对象 AND,会发生错误
    • 这些错误不会阻止处理另外的输入对象IN PRINCIPLE(在情境上,可能没有输入对象和/或先前的输入对象可能已经成功处理).
      • 在先进的功能,使用$PSCmdlet.WriteError()报告非终止错误(Write-Error不幸的是,不会导致$?被设置为$False调用者的范围-看到这个GitHub的问题).
      • 处理非终止错误:$?告诉您最近的命令是否报告了至少一个非终止错误.
        • 因此,$?存在$False可以意味着输入对象的任何(非空)子集未被正确处理,可能意味着整个集合.
        • 首选变量$ErrorActionPreference和/或公共cmdlet参数-ErrorAction可以根据错误输出行为修改非终止错误的行为(仅限),以及是否应将非终止错误升级为脚本 - 终止错误.
  • 所有其他情况下报告STATEMENT-TERMINATING错误.

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

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

  • 虽然不太可能,但New-TemporaryFile(PSv5 +)如果失败则会报告非终止错误,尽管不接受管道输入并且只生成一个输出对象 - 但这可能会在v6中发生变化,但是:请参阅此GitHub问题.

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


Ryn*_*ant 7

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

尝试使用...抓住Write-Error:

try {
    SomeFunction -ErrorAction Stop
}
catch {
    DoSomething
}
Run Code Online (Sandbox Code Playgroud)


MiF*_*vil 6

除了Andy Arismendi的回答:

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

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

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

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

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

要将Write-Error用作非终止命令而不考虑其他环境设置,可以使用带有值的通用参数 :-ErrorActionContinue

 Write-Error "Error Message" -ErrorAction:Continue
Run Code Online (Sandbox Code Playgroud)