你是否曾经运行过一个脚本或 PowerShell 命令,并被下面显示的一堆红色的文字——如下所示——所淹没?

错误可能会让人感到不知所措和困惑。而且,大多数情况下,错误往往很难阅读,这使得确定脚本出了什么问题以及问题出在哪里几乎是不可能的。
幸运的是,在 PowerShell 中有一些选项可以通过错误处理来改善这种情况。使用错误处理,错误可以被过滤并以更容易理解的方式显示出来。而且,理解错误可以轻松地为错误处理添加更多逻辑。
在本文中,你将学习 PowerShell 中的错误以及如何拦截它们以执行错误处理,使用 PowerShell 的 Try Catch
块(以及 finally
块)。
了解 PowerShell 中错误的工作原理
在深入讨论错误处理之前,让我们先了解 PowerShell 中的一些错误概念。理解错误可以带来更好的错误处理策略。
$Error
自动变量
在 PowerShell 中,有很多 自动变量,其中一个是 $Error
自动变量。PowerShell 使用 $Error
变量来存储会话中遇到的所有错误。$Error
变量是一个按最近发生排序的错误数组。
当您首次打开 PowerShell 会话时,$Error
变量为空。您可以通过调用 $Error
变量来检查它。

正如您所见,$Error
变量最初为空。但是,一旦发生错误,该错误将被添加并存储到 $Error
变量中。
在下面的示例中,错误是通过故意获取一个不存在的服务名称而生成的。

从上面的输出中,您可以看到生成的错误已添加到 $Error
变量中。
$Error 变量包含在 PowerShell 会话中生成的错误集合。可以通过调用其数组位置来访问每个错误。最新的错误将始终位于索引 0。
例如,可以使用
$Error[0]
检索最新的错误。
$Error
对象属性
由于 PowerShell 中的所有内容都是对象,$Error
变量是一个对象,而对象具有属性。将 $Error
变量传递到 Get-Member
cmdlet,您应该会看到可用属性的列表。

为了确定错误的原因,您可以使用下面的命令查看 InvocationInfo
属性的内容。

现在,您可以对其他属性执行相同的操作,并发现您可以找到的其他信息!
终止错误
终止错误在PowerShell中遇到时会停止执行流程,而非终止错误则不会。终止错误可能发生在多种情况下。一个例子是当你使用不存在的参数调用cmdlet时。
如下面的截图所示,当运行命令Get-Process notepad
时,该命令是有效的,并显示了notepad进程的详细信息。

但是,当使用不存在的参数时,比如Get-Process notepad -handle 251
,cmdlet会显示一个错误,指出handle
参数无效。然后,cmdlet在不显示notepad
进程的详细信息的情况下退出。

非终止错误
非终止错误是不会停止脚本或命令执行的错误。例如,看下面的代码。这段代码从fileslist.txt文件中获取文件名列表。然后,脚本遍历每个文件名,读取每个文件的内容,并在屏幕上输出。
filelist.txt文件的内容是下面列表中显示的文件名。
但是如果File_6.log实际上不存在呢?当你运行代码时,你会期待会发生错误,因为脚本找不到File_6.log文件。你会看到类似下面显示的输出。

正如您从上面的结果截图所看到的那样,脚本能够读取列表中的前五个文件,但当它尝试读取文件File_6.txt时,会返回一个错误。然后脚本继续读取其余的文件然后退出。它没有终止。
变量$ErrorActionPreference
到目前为止,您已经了解了终止和非终止错误以及它们之间的区别。但是,您是否知道非终止错误可以被强制视为终止错误吗?
PowerShell有一个称为p参考变量的概念。这些变量用于改变PowerShell在许多不同方面的行为。其中一个变量称为$ErrorActionPreference
。
变量$ErrorActionPreference
用于更改PowerShell处理非终止错误的方式。默认情况下,$ErrorActionPreference
的值设置为Continue
。将$ErrorActionPreference
变量的值更改为STOP
会强制PowerShell将所有错误视为终止错误。
使用下面的代码来更改$ErrorActionPreference
的值。
要了解更多关于其他有效的$ErrorActionPreference变量值,请访问PowerShell ErrorActionPreference。
现在,请参考本文中非终止错误部分中使用的示例。可以修改脚本以包含对$ErrorActionPreference
的更改,就像下面显示的代码一样:
运行上面修改后的代码会与以前不同,当$ErrorActionPreference
值设置为默认值Continue
时。

$ErrorActionPreference
variable从上面结果的截图中可以看出,脚本能够读取列表中的前五个文件,但当它尝试读取文件File_6.txt时,返回错误,因为找不到该文件。然后,脚本终止,其余文件也不被读取。
$ErrorActionPreference
值仅在当前的PowerShell会话中有效。一旦启动新的PowerShell会话,它就会重置为默认值。
ErrorAction
通用参数
如果将$ErrorActionPreference
值应用于PowerShell会话,则ErrorAction
参数将应用于支持通用参数的任何cmdlet。 ErrorAction
参数接受与$ErrorActionPreference
变量相同的值。
ErrorAction
参数值优先于$ErrorActionPreference
值。
让我们回到上一个例子中使用相同的代码。但是,这次,ErrorAction
参数添加到Get-Content
行。
运行修改后的代码后,您会发现即使$ErrorActionPreference
设置为Continue
,脚本仍在遇到错误时终止。脚本终止是因为在Get-Content
中,PowerShell ErrorAction
参数值设置为STOP
。

ErrorAction
parameter使用PowerShell Try Catch Blocks
到目前为止,您已经了解了PowerShell错误以及$ErrorActionPreference
变量和PowerShell ErrorAction
参数的工作原理。现在,是时候学习一些有用的东西了——PowerShell Try Catch Finally
块。
PowerShell的try catch
代码块(以及可选的finally块
)是将代码片段包裹起来并捕获任何返回错误的一种方法。
下面的代码显示了Try
语句的语法。
Try
块包含您希望PowerShell“尝试”并监视错误的代码。如果Try
块中的代码遇到错误,该错误将被添加到$Error
变量中,然后传递给Catch
块。
Catch
块包含在从Try
块接收错误时要执行的操作。在Try
语句中可以有多个Catch
块。
Finally
块包含在Try
语句的末尾运行的代码。无论是否遇到错误,此块都会运行。
使用PowerShell ErrorAction捕获非特定错误(Catch-All)
A simple Try
statement contains a Try
and a Catch
block. The Finally
block is optional.
例如,要捕获非特定异常,Catch
参数应为空。下面的示例代码使用了与$ErrorActionPreference变量部分相同的脚本,但修改为使用Try Catch
块。
从下面的代码中可以看到,这次foreach
语句被包含在Try
块中。然后,Catch
块包含了如果发生错误则显示字符串发生错误
的代码。Finally
块中的代码只是清除$Error
变量。
上面的代码在 PowerShell 中运行后,将给出下面显示的输出。

上面的输出表明脚本遇到了一个错误,运行了Catch
块中的代码,然后终止了。
错误已被处理,这就是错误处理的目的。但是,显示的错误过于泛化。为了显示更详细的错误,您可以访问由Try
块传递的错误的Exception
属性。
下面的代码已经修改,特别是Catch
块中的代码,以显示从当前错误传递的管道中获取的异常消息 – $PSItem.Exception.Message
当运行上面修改后的代码时,显示的消息会更加详细。

捕获特定错误
有时,通用的错误处理并不是最合适的方法。也许,您希望脚本执行一个取决于遇到的错误类型的操作。
如何确定错误类型?通过检查上一个错误的Exception
属性的TypeName
值。例如,要查找前面示例中的错误类型,请使用以下命令:
上面代码的结果将会像下面的屏幕截图一样。正如您所见,TypeName
值被显示为 – System.Management.Automation.ItemNotFoundException
。

现在您已经知道需要拦截的错误类型,修改代码以特定地捕获它。如下修改后的代码所示,现在有两个 `Catch` 块。第一个 `Catch` 块拦截特定类型的错误 (`System.Management.Automation.ItemNotFoundException`)。相反,第二个 `Catch` 块包含通用的、捕获所有错误的消息。
下面的截图显示了上述修改后的代码的输出。

结论
在本文中,您了解了 PowerShell 中的错误、其属性以及如何确定错误的具体类型。您还了解了 `$ErrorActionPreference` 变量和 PowerShell `ErrorAction` 参数如何影响 PowerShell 处理非终止错误的方式。
您还学会了如何使用 PowerShell 的 `Try Catch Finally` 块执行错误处理,无论是针对特定错误还是通用方法。
本文中展示的示例仅演示了 `Try Catch Finally` 块的基本工作原理。我希望您在本文中获得的知识可以为您开始在脚本中应用错误处理提供起点。