在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。有区别吗?
答案 0 :(得分:157)
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 try/catch
: about_Try_Catch_Finally trap
: about_Trap throw
: about_Throw return
: about_Return 答案 1 :(得分:15)
PowerShell中Write-Error cmdlet和throw关键字之间的主要区别在于前者只是打印一些文本到standard error stream (stderr),而后者实际上终止正在运行的命令或函数的处理,即PowerShell通过向控制台发送有关错误的信息来处理然后。
您可以在您提供的示例中观察两者的不同行为:
$URL_Format_Error = [string]"..."
Write-Error $URL_Format_Error
return
在此示例中,{em>显式已将return
关键字添加到后,在错误消息发送到控制台后停止执行脚本。另一方面,在第二个示例中,return
关键字不是必需的,因为终止是由throw
隐式完成的:
$URL_Format_Error = New-Object System.FormatException "..."
Throw $URL_Format_Error
答案 2 :(得分:10)
重要: 2 类型的终止错误,当前的帮助主题很遗憾 conflate :< / p>
语句 -terminating 错误,由cmdlet在某些不可恢复的情况下以及发生.NET异常/ PS运行时错误的表达式报告;只有语句被终止,脚本默认继续执行。
脚本 - 终止错误,由Throw
触发或通过错误操作首选项变量升级其他错误类型之一/参数值Stop
。
除非被捕获,否则它们会终止当前执行的线程(即,不仅仅是当前脚本,还包括所有调用者,如果适用的话)。
有关PowerShell错误处理的全面概述,请参阅this GitHub documentation issue。
本文的其余部分重点介绍非终止与语句终止错误。
为了补充现有的有用答案,重点关注问题的核心:如何选择是否报告语句 - 终止或非终止错误?
Cmdlet Error Reporting包含有用的指南;让我尝试实用的摘要:
非终止错误背后的一般思想是允许对大型输入集进行“容错”处理:无法处理输入对象的子集(默认情况下)中止 - 可能长时间运行 - 整个流程,允许您检查错误,稍后仅重新处理失败的对象 - 通过错误报告在自动变量$Error
中收集的记录。
报告非终止错误,如果您的cmdlet /高级功能:
$PSCmdlet.WriteError()
报告非终止错误(Write-Error
,遗憾的是,$?
不会导致$False
设置为$?
em>来电者的范围 - 见this GitHub issue)。$?
告诉您最近的命令是否报告至少一个非终止错误。
$False
为$ErrorActionPreference
可能意味着输入对象的任何(非空)子集未正确处理,可能是整个集合。-ErrorAction
和/或公共cmdlet参数$PSCmdlet.ThrowTerminatingError()
可以根据错误输出行为修改非终止错误的行为,以及是否应将非终止错误升级为< em> script -terminating ones。在所有其他情况中报告声明终止错误。
Throw
才能生成语句终止错误。 try/catch
关键字会生成脚本 - 终止错误,该错误会中止整个脚本。trap
处理程序或$?
语句(无法与一起使用非终止错误),但请注意,默认情况下,即使语句 - 终止错误也不会阻止脚本的其余部分运行。与非终止错误一样,如果前一个语句触发了语句终止错误,则$False
会反映New-TemporaryFile
。可悲的是,并非所有PowerShell自己的核心cmdlet都遵循这些规则:
虽然不太可能,Resume-Job
(PSv5 +)会报告一个非终止错误,如果它失败了,尽管不接受管道输入并且只生成一个输出对象 - 这很可能但是,在v6中更改:请参阅this GitHub issue。
Start-Job
的帮助声称传递了不受支持的作业类型(例如使用Resume-Job
创建的作业,但不受支持,因为{{1}}仅适用于 workflow jobs)会导致终止错误,但从PSv5.1开始就不是这样。
答案 3 :(得分:7)
Write-Error
允许函数的使用者使用-ErrorAction SilentlyContinue
(或-ea 0
)来抑制错误消息。虽然throw
需要try{...} catch {..}
使用try {catch Write-Error
:
try {
SomeFunction -ErrorAction Stop
}
catch {
DoSomething
}
答案 4 :(得分:5)
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用作非终止命令而不考虑其他环境设置,您可以使用值-ErrorAction
的{{3}} Continue
:
Write-Error "Error Message" -ErrorAction:Continue
答案 5 :(得分:0)
如果您的代码阅读正确,那么您是对的。终止错误应使用throw
,如果您正在处理.NET类型,那么遵循.NET异常约定也很有帮助。