为了补充基思希尔的有用答案:
PowerShell 的错误处理很复杂,而且由于文档将两种类型的终止错误混为一谈,事情变得更糟:
语句终止错误:
- 默认情况下,它们仅中止当前语句并继续执行(使用下一条语句)。
脚本终止错误:
- 默认情况下,它们是致命的:它们中止整个脚本(更准确地说,是整个调用堆栈);直接在 PowerShell 中生成它们的唯一方法是使用
Throw
语句。
顺便说一句:第三类错误是非终止错误,其主要目的是允许管道处理 cmdlet 报告与特定输入对象有关的错误,其中此类错误不会绝对阻止进一步输入对象的潜在成功处理。
未处理的 .NET 异常是语句终止错误,就像cmdlet报告的语句终止错误一样。
因此,您不能从根本上使用try
/以不同的方式处理它们catch
(它作用于语句终止和脚本终止错误,但非终止错误):
虽然您可以测试块中可用的实例,因为它的属性[ErrorRecord]
类型为
( ),
但要恢复 cmdlet 引发的语句终止错误的默认行为为时已晚,因为块已经退出。$_
catch
.Exception
[System.Management.Automation.MethodInvocationExeption]
$_.Exception -is [System.Management.Automation.MethodInvocationExeption]
try
将所有语句终止错误视为致命错误:
我想终止上述 .NET 调用的所有异常。我还想终止 cmdlet 的终止错误。我不想因 cmdlet 的非终止错误而终止。
在您的脚本中使用以下内容:
# Escalate any statement-terminating error to a script-terminating one,
# but leave non-terminating ones alone.
# (Already script-terminating errors still terminate the script.)
trap { break }
trap
,如try
/ catch
,作用于语句终止和脚本终止错误(但不是非终止错误)。
break
为了终止脚本,使用是至关重要的;默认情况下,或者当您使用 时,会继续continue
执行导致错误的语句之后的下一条语句,这甚至适用于脚本终止错误;另外抑制错误记录的输出(尽管它仍然在自动收集中收集)。continue
$Error
将所有错误视为致命错误:
注意:外部程序通过其退出代码发出的失败信号,反映在自动变量$LASTEXITCODE
中, PowerShell从不将其视为错误(但是,正在讨论更改的选择加入机制;此外,当外部程序的 stderr 输出时,存在极端情况间接触发 PowerShell 错误 - 请参阅此 GitHub 问题)。
环境:
# !! Covers many, but NOT ALL scenarios - see below.
$ErrorActionPreference = 'Stop'
几乎足以使所有错误类型默认为致命(脚本终止),但遗憾的是,并非在所有情况下都如此。
注意:记录的行为声称$ErrorActionPreference
偏好变量仅作用于非终止错误,但实际上它也作用于语句终止错误;相比之下,公共参数确实只作用于非终止错误。
因此,您不需要将偏好变量与.-ErrorAction
trap { break }
假设,因此默认情况下,上述所有PowerShell 错误都是致命的,同时允许您有 选择地覆盖该行为:
- 对于非终止错误:在每个命令的基础上使用公共参数
-ErrorAction Continue/SilentlyContinue/Ignore
(再次注意,这不适用于语句/脚本终止错误)
- 对于语句/脚本终止错误:带有命令封闭
try
/catch
然而,在实践中,对于碰巧在脚本模块中作为高级功能实现的命令,该方法会失败,其中包括生成的脚本模块,这些脚本模块包装远程 cmdlet 调用以进行隐式远程处理:
由于变量范围在脚本模块中的工作方式,位于(不同)脚本模块中的函数看不到调用者的首选项变量(编译的 cmdlet 没有这个问题):请参阅这个 GitHub 问题。
换句话说:
- 来自不同模块的函数将有效地忽略调用者的
$ErrorActionPreference = 'Stop'
值。
- 您甚至可能看不到它的到来,因为不清楚哪些命令是作为二进制 cmdlet 实现的,哪些是作为函数实现的。
- 即使您确实知道哪些命令需要解决方法,该解决方法也很麻烦 - 请参阅我的这个答案。
这个GitHub 文档问题试图全面概述 PowerShell 的错误处理及其陷阱。