查看 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。有区别吗?
return
确实 不 在(高级)函数的 process
块中返回调用者;相反,它继续到管道中的下一个输入对象。实际上,这是生成非终止错误的典型场景:如果仍然可以处理进一步的输入对象。
Throw
会生成一个 script 终止错误,这与由 Get-Item -NoSuchParameter
或 1 / 0
触发的 statement 终止错误不同.
如果您想通知用户非严重错误,则应使用 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
}
看起来该函数的作者想要停止执行该函数并在屏幕上显示一条错误消息,但不希望整个脚本停止执行。脚本作者可以使用 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
这里的一个问题是将 return
与 ForEach-Object
一起使用。它不会像人们预期的那样中断处理。
更多信息:
$ErrorActionPreference: about_Preference_Variables
尝试/捕获:about_Try_Catch_Finally
陷阱:about_Trap
抛出:about_Throw
返回:about_Return
重要提示:有 2 种类型的终止错误,不幸的是,当前的帮助主题将它们混为一谈:
语句终止错误,由 cmdlet 在某些不可恢复的情况下报告,以及由发生 .NET 异常/PS 运行时错误的表达式报告;只有语句被终止,默认情况下脚本继续执行。
脚本终止错误(更准确地说:runspace-terminating),由 Throw 触发或通过常见的 -ErrorAction 参数 -ErrorAction Stop 或通过 $ErrorActionPreference 首选项变量 $ErrorActionPreference = ' 升级其他错误类型之一停止'。除非被捕获,否则它们会终止当前的运行空间(线程);也就是说,它们不仅会终止当前脚本,还会终止所有调用者(如果适用)。
有关 PowerShell 错误处理的全面概述,请参阅 GitHub documentation issue #1583。
这篇文章的其余部分侧重于非终止与语句终止错误。
为了补充现有的有用答案,重点关注问题的核心:您如何选择报告语句终止或非终止错误?
Cmdlet Error Reporting 包含有用的指南;让我尝试一个实用的总结:
非终止错误背后的一般思想是允许对大型输入集进行“容错”处理:无法处理子集输入对象不应(默认情况下)中止 - 可能长时间运行的 - 进程作为一个整体,允许您检查错误并仅重新处理 失败 对象稍后 - 通过自动变量 $Error
中收集的错误记录报告。
如果您的 cmdlet/高级功能:通过管道输入和/或数组值参数接受多个输入对象,并且特定输入对象发生错误,并且这些错误不会阻止进一步输入对象的处理,则报告非终止错误原则上(在某种情况下,可能没有输入对象剩下和/或之前的输入对象可能已经被成功处理)。在高级函数中,使用 $PSCmdlet.WriteError() 报告非终止错误(不幸的是,Write-Error 不会导致 $? 在调用者的范围内设置为 $False - 请参阅 GitHub 问题 #3629)。处理非终止错误:$?告诉您最近的命令是否报告了至少一个非终止错误。因此,$?为 $False 可能意味着输入对象的任何(非空)子集都没有得到正确处理,可能是整个集合。首选项变量 $ErrorActionPreference 和/或通用 cmdlet 参数 -ErrorAction 可以(仅)根据错误输出行为以及是否应将非终止错误升级为脚本终止错误来修改非终止错误的行为。
通过管道输入和/或数组值参数接受多个输入对象,并且
特定输入对象发生错误,并且
这些错误在原则上不会阻止对更多输入对象的处理(在某种情况下,可能没有剩余输入对象和/或之前的输入对象可能已经被成功处理)。在高级函数中,使用 $PSCmdlet.WriteError() 报告非终止错误(不幸的是,Write-Error 不会导致 $? 在调用者的范围内设置为 $False - 请参阅 GitHub 问题 #3629)。处理非终止错误:$?告诉您最近的命令是否报告了至少一个非终止错误。因此,$?为 $False 可能意味着输入对象的任何(非空)子集都没有得到正确处理,可能是整个集合。首选项变量 $ErrorActionPreference 和/或通用 cmdlet 参数 -ErrorAction 可以(仅)根据错误输出行为以及是否应将非终止错误升级为脚本终止错误来修改非终止错误的行为。
在高级函数中,使用 $PSCmdlet.WriteError() 报告非终止错误(不幸的是,Write-Error 不会导致 $? 在调用者的范围内设置为 $False - 请参阅 GitHub 问题 #3629)。
处理非终止错误:$?告诉您最近的命令是否报告了至少一个非终止错误。因此,$?为 $False 可能意味着输入对象的任何(非空)子集都没有得到正确处理,可能是整个集合。首选项变量 $ErrorActionPreference 和/或通用 cmdlet 参数 -ErrorAction 可以(仅)根据错误输出行为以及是否应将非终止错误升级为脚本终止错误来修改非终止错误的行为。
因此,$?为 $False 可能意味着输入对象的任何(非空)子集都没有得到正确处理,可能是整个集合。
首选项变量 $ErrorActionPreference 和/或通用 cmdlet 参数 -ErrorAction 可以(仅)根据错误输出行为以及是否应将非终止错误升级为脚本终止错误来修改非终止错误的行为。
在所有其他情况下报告 STATEMENT-TERMINATING 错误。值得注意的是,如果在仅接受 SINGLE 或 NO 输入对象并输出 NO 或 SINGLE 输出对象或仅接受参数输入且给定的参数值阻止有意义的操作的 cmdlet/高级函数中发生错误。在高级函数中,您必须使用 $PSCmdlet.ThrowTerminatingError() 才能生成语句终止错误。请注意,相比之下,Throw 关键字会生成一个脚本终止错误,该错误会中止整个脚本(技术上是:当前运行空间(线程))。处理语句终止错误:可以使用 try/catch 处理程序或陷阱语句(不能与非终止错误一起使用),但请注意,即使默认情况下语句终止错误也不会阻止脚本的其余部分运行.与非终止错误一样,$?如果前一个语句触发了语句终止错误,则反映 $False。
值得注意的是,如果在仅接受 SINGLE 或 NO 输入对象并输出 NO 或 SINGLE 输出对象或仅接受参数输入的 cmdlet/高级函数中发生错误,并且给定的参数值会阻止有意义的操作。在高级函数中,您必须使用 $PSCmdlet.ThrowTerminatingError() 才能生成语句终止错误。请注意,相比之下,Throw 关键字会生成一个脚本终止错误,该错误会中止整个脚本(技术上是:当前运行空间(线程))。处理语句终止错误:可以使用 try/catch 处理程序或陷阱语句(不能与非终止错误一起使用),但请注意,即使默认情况下语句终止错误也不会阻止脚本的其余部分运行.与非终止错误一样,$?如果前一个语句触发了语句终止错误,则反映 $False。
在高级函数中,您必须使用 $PSCmdlet.ThrowTerminatingError() 才能生成语句终止错误。
请注意,相比之下,Throw 关键字会生成一个脚本终止错误,该错误会中止整个脚本(技术上是:当前运行空间(线程))。
处理语句终止错误:可以使用 try/catch 处理程序或陷阱语句(不能与非终止错误一起使用),但请注意,即使默认情况下语句终止错误也不会阻止脚本的其余部分运行.与非终止错误一样,$?如果前一个语句触发了语句终止错误,则反映 $False。
并非所有 PowerShell 自己的核心 cmdlet 都遵循以下规则:
虽然不太可能失败,但 New-TemporaryFile (PSv5+) 会在失败时报告非终止错误,尽管不接受管道输入并且只生成一个输出对象 - 这至少在 PowerShell [Core] 7.0 中已得到纠正,但是:请参阅 GitHub 问题 #4634。
Resume-Job 的帮助声称传递不受支持的作业类型(例如使用 Start-Job 创建的作业,不支持,因为 Resume-Job 仅适用于工作流作业)会导致终止错误,但截至目前并非如此PSv5.1。
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
[System.Management.Automation.ErrorRecord]
实例,默认情况下收集在自动 $Error
集合中($Error[0]
包含最近的错误)。即使您只是将 Write-Error
与 string 一起使用,该字符串也会被包装在 [System.Management.Automation.ErrorRecord]
实例中。
对 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 用作非终止命令而不考虑其他环境设置,您可以使用 common parameter -ErrorAction
和值 Continue
:
Write-Error "Error Message" -ErrorAction:Continue
Write-Error
允许函数的使用者使用 -ErrorAction SilentlyContinue
(或者 -ea 0
)抑制错误消息。而 throw
需要 try{...} catch {..}
要将 try...catch 与 Write-Error
一起使用:
try {
SomeFunction -ErrorAction Stop
}
catch {
DoSomething
}
如果您对代码的阅读是正确的,那么您是正确的。终止错误应该使用 throw
,如果您正在处理 .NET 类型,那么遵循 .NET 异常约定也会很有帮助。
不定期副业成功案例分享
return
的解释更新了我的答案。