什么时候应该使用写错误还是抛出? 终止还是非终止错误

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

187833 次浏览

在 PowerShell 中,写入错误 cmdlet 和 关键字之间的主要区别在于,前者只是将一些文本 指纹发送给 标准错误流(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。默认情况下,它所做的只是在控制台上以红色文本打印一条错误消息。它不会阻止管道或循环继续。另一方面,Throw产生所谓的终止错误。如果使用 throw,则管道和/或当前循环将终止。实际上,除非使用 traptry/catch结构来处理终止错误,否则所有执行都将被终止。

有一件事要注意,如果你设置 ABC0呼叫 "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,它不会像人们期望的那样中断处理。

更多信息:

  • $ErrorActionPreference: 关于偏好变量(Preferences _ Variables)
  • try/catch: 译自: 美国《科学》杂志网站(http://technet.microsoft.com/en-us/library/dd315350.aspx)原著: http://technet.microsoft.com/en-us/library/dd315350.aspx
  • trap: Http://technet.microsoft.com/en-us/library/dd347548.aspx”rel = “ noReferrer”> about _ Trap
  • throw: 翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳翻译: 奇芳
  • return: 返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文章页面返回文

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

要使用 try... ... 用 Write-Error捕捉:

try {
SomeFunction -ErrorAction Stop
}
catch {
DoSomething
}

如果你对代码的理解是正确的,那么你就是正确的。终止错误应该使用 throw,如果您正在处理。NET 类型,那么遵循以下步骤也是有帮助的。NET 异常约定。

重要 : 有 < em > 2 终止错误类型,不幸的是,当前的帮助主题 合并:

  • 语句 -终止 错误,正如 cmdlet 在某些不可恢复的情况下以及在其中。NET 异常/PS 运行时发生错误; 只有 声明被终止,而 默认情况下继续执行脚本

  • Script -终止 错误(更准确地说: < em > runspace -终止) ,由 Throw升级通过 通用 -ErrorAction参数-ErrorAction Stop或通过 $ErrorActionPreference偏好变量$ErrorActionPreference = 'Stop'触发的其他错误类型之一。
    除非被捕获,否则它们会终止当前的 runspace (线程) ; 也就是说,它们不仅会终止当前的脚本,而且还会终止它的所有调用者(如果适用的话)。

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

本文的其余部分将集中讨论 没有终止声明终止错误。


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

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

没有终止错误背后的基本思想是允许对大型输入集进行“容错”处理 : 未能处理输入对象的 子集不应该(默认情况下)终止可能长时间运行的进程 作为一个整体,允许您检查错误和 稍后只重新处理 < em > 失败的 对象——正如通过在自动变量 $Error中收集的错误记录所报告的那样。

  • 如果您的 cmdlet/Advanced 函数:

    • 通过管道输入和/或数组值参数接受多个输入对象 ,AND
    • 对于 SPECIFIC 输入对象 ,AND 会出现错误
    • 这些错误不能阻止在原则上进一步处理输入对象 (情况而已,可能已经没有输入对象了,或者以前的输入对象可能已经被成功处理了)。
      • 在高级函数中,使用 $PSCmdlet.WriteError()报告非终止错误(不幸的是,Write-Error不会导致 打电话的人作用域中的 $?设置为 $False-参见 GitHub 第3629期)。
      • 处理 非终止错误: $?告诉您最近的命令是否报告了 至少一个非终止错误。
        • 因此,$?$False可能意味着输入对象的 任何(非空)子集没有被正确处理,可能是整个集合。
        • 偏好变量 $ErrorActionPreference和/或公共 cmdlet 参数 -ErrorAction可以修改非终止性错误的行为(仅)在错误输出行为方面,以及是否应该将非终止性错误升级到 剧本终止性错误。
  • 所有其他案子中报告一个 声明终止错误。

    • 值得注意的是,如果仅接受 SINGLE 或 NO 输入对象并输出 NO 或 SINGLE 输出对象或接受 只输入参数且给定的参数值阻止有意义的操作的 cmdlet/Advanced 函数中发生错误,则为 < strong > 。
      • 在高级函数中,必须使用 $PSCmdlet.ThrowTerminatingError()才能生成语句终止错误。
      • 请注意,相比之下,Throw关键字会生成一个 剧本终止错误,该错误将中止 整个剧本(技术上是当前的 运行空间(线程))。
      • 处理 语句终止错误: 可以使用 try/catch处理程序或 trap语句(不能没有终止错误一起使用) ,但是请注意,即使在默认情况下 声明终止错误也不会阻止脚本的其余部分运行。与 没有终止错误一样,如果前面的语句触发了语句终止错误,则 $?反映 $False

并非所有 PowerShell 自己的核心 cmdlet 都遵循这些规则 :

  • 虽然不太可能失败,但如果失败,New-TemporaryFile(PSv5 +)将报告一个非终止性错误,尽管它不接受管道输入,只生成 输出对象——这至少在 PowerShell [ Core ]7.0时已经得到纠正: 参见 GitHub 第4634期

  • Resume-Job 的帮助声称,传递一个不受支持的作业类型(比如用 Start-Job创建的作业,它不受支持,因为 Resume-Job只应用于 工作流程作业)会导致终止错误,但是在 PSv5.1中并非如此。

除了 Andy Arismendi 的回答:

写入错误是否终止进程取决于 $ErrorActionPreference设置。

对于非平凡的脚本,$ErrorActionPreference = "Stop"是一个快速失败的 建议的设置

”PowerShell 针对错误的默认行为是 继续错误... 感觉非常 VB6“在错误恢复下一步”-什么

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

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

不管其他环境设置如何,要使用 写入错误作为非终止命令,可以使用值为 Continue共同参数 -ErrorAction:

 Write-Error "Error Message" -ErrorAction:Continue