掌握 PowerShell 的 Try Catch 块及示例

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

Example of errors in PowerShell

错误可能会让人感到不知所措和困惑。而且,大多数情况下,错误往往很难阅读,这使得确定脚本出了什么问题以及问题出在哪里几乎是不可能的。

幸运的是,在 PowerShell 中有一些选项可以通过错误处理来改善这种情况。使用错误处理,错误可以被过滤并以更容易理解的方式显示出来。而且,理解错误可以轻松地为错误处理添加更多逻辑。

在本文中,你将学习 PowerShell 中的错误以及如何拦截它们以执行错误处理,使用 PowerShell 的 Try Catch 块(以及 finally 块)。

了解 PowerShell 中错误的工作原理

在深入讨论错误处理之前,让我们先了解 PowerShell 中的一些错误概念。理解错误可以带来更好的错误处理策略。

$Error 自动变量

在 PowerShell 中,有很多 自动变量,其中一个是 $Error 自动变量。PowerShell 使用 $Error 变量来存储会话中遇到的所有错误。$Error 变量是一个按最近发生排序的错误数组。

当您首次打开 PowerShell 会话时,$Error 变量为空。您可以通过调用 $Error 变量来检查它。

The $Error variable is empty

正如您所见,$Error 变量最初为空。但是,一旦发生错误,该错误将被添加并存储到 $Error 变量中。

在下面的示例中,错误是通过故意获取一个不存在的服务名称而生成的。

PS> Get-Service xyz
PS> $Error
PS> $Error.Count
The error is added to the $Error variable

从上面的输出中,您可以看到生成的错误已添加到 $Error 变量中。

$Error 变量包含在 PowerShell 会话中生成的错误集合。可以通过调用其数组位置来访问每个错误。最新的错误将始终位于索引 0。

例如,可以使用 $Error[0] 检索最新的错误。

$Error 对象属性

由于 PowerShell 中的所有内容都是对象,$Error 变量是一个对象,而对象具有属性。将 $Error 变量传递到 Get-Member cmdlet,您应该会看到可用属性的列表。

$Error | Get-Member
The $Error object properties

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

$Error[0].InvocationInfo
The InvocationInfo property

现在,您可以对其他属性执行相同的操作,并发现您可以找到的其他信息!

终止错误

终止错误在PowerShell中遇到时会停止执行流程,而非终止错误则不会。终止错误可能发生在多种情况下。一个例子是当你使用不存在的参数调用cmdlet时。

如下面的截图所示,当运行命令Get-Process notepad时,该命令是有效的,并显示了notepad进程的详细信息。

The notepad process details

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

Error is thrown because the parameter is invalid

非终止错误

非终止错误是不会停止脚本或命令执行的错误。例如,看下面的代码。这段代码从fileslist.txt文件中获取文件名列表。然后,脚本遍历每个文件名,读取每个文件的内容,并在屏幕上输出。

$file_list =  Get-Content .\filelist.txt
foreach ($file in $file_list) {
    Write-Output "Reading file $file"
    Get-Content $file
}

filelist.txt文件的内容是下面列表中显示的文件名。

File_1.log
File_2.log
File_3.log
File_4.log
File_5.log
File_6.log
File_7.log
File_8.log
File_9.log
File_10.log

但是如果File_6.log实际上不存在呢?当你运行代码时,你会期待会发生错误,因为脚本找不到File_6.log文件。你会看到类似下面显示的输出。

Example of non-terminating error

正如您从上面的结果截图所看到的那样,脚本能够读取列表中的前五个文件,但当它尝试读取文件File_6.txt时,会返回一个错误。然后脚本继续读取其余的文件然后退出。它没有终止

变量$ErrorActionPreference

到目前为止,您已经了解了终止和非终止错误以及它们之间的区别。但是,您是否知道非终止错误可以被强制视为终止错误吗?

PowerShell有一个称为p参考变量的概念。这些变量用于改变PowerShell在许多不同方面的行为。其中一个变量称为$ErrorActionPreference

变量$ErrorActionPreference用于更改PowerShell处理非终止错误的方式。默认情况下,$ErrorActionPreference的值设置为Continue。将$ErrorActionPreference变量的值更改为STOP会强制PowerShell将所有错误视为终止错误。

使用下面的代码来更改$ErrorActionPreference的值。

$ErrorActionPreference = "STOP"

要了解更多关于其他有效的$ErrorActionPreference变量值,请访问PowerShell ErrorActionPreference

现在,请参考本文中非终止错误部分中使用的示例。可以修改脚本以包含对$ErrorActionPreference的更改,就像下面显示的代码一样:

# 将$ErrorActionPreference值设置为STOP
$ErrorActionPreference = "STOP"
$file_list =  Get-Content .\filelist.txt
foreach ($file in $file_list) {
    Write-Output "Reading file $file"
    Get-Content $file
}

运行上面修改后的代码会与以前不同,当$ErrorActionPreference值设置为默认值Continue时。

Forcing a terminating error using the $ErrorActionPreference variable

从上面结果的截图中可以看出,脚本能够读取列表中的前五个文件,但当它尝试读取文件File_6.txt时,返回错误,因为找不到该文件。然后,脚本终止,其余文件也不被读取。

$ErrorActionPreference值仅在当前的PowerShell会话中有效。一旦启动新的PowerShell会话,它就会重置为默认值。

ErrorAction通用参数

如果将$ErrorActionPreference值应用于PowerShell会话,则ErrorAction参数将应用于支持通用参数的任何cmdlet。 ErrorAction参数接受与$ErrorActionPreference变量相同的值。

ErrorAction参数值优先于$ErrorActionPreference值。

让我们回到上一个例子中使用相同的代码。但是,这次,ErrorAction参数添加到Get-Content行。

# 将$ErrorActionPreference值设置为默认(CONTINUE)
$ErrorActionPreference = "CONTINUE"
$file_list =  Get-Content .\filelist.txt
foreach ($file in $file_list) {
    Write-Output "Reading file $file"
		# 使用-ErrorAction通用参数
		Get-Content $file -ErrorAction STOP
}

运行修改后的代码后,您会发现即使$ErrorActionPreference设置为Continue,脚本仍在遇到错误时终止。脚本终止是因为在Get-Content中,PowerShell ErrorAction参数值设置为STOP

Forcing a terminating error using the PowerShell ErrorAction parameter

使用PowerShell Try Catch Blocks

到目前为止,您已经了解了PowerShell错误以及$ErrorActionPreference变量和PowerShell ErrorAction参数的工作原理。现在,是时候学习一些有用的东西了——PowerShell Try Catch Finally块。

PowerShell的try catch代码块(以及可选的finally块)是将代码片段包裹起来并捕获任何返回错误的一种方法。

下面的代码显示了Try语句的语法。

try {
    <statement list>
}
catch [[<error type>][',' <error type>]*]{
    <statement list>
}
finally {
    <statement list>
}

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变量。

$file_list = Get-Content .\filelist.txt
try {
    foreach ($file in $file_list) {
        Write-Output "Reading file $file"
        Get-Content $file -ErrorAction STOP
    }
}
catch {
    Write-Host "An Error Occured" -ForegroundColor RED
}
finally {
    $Error.Clear()
}

上面的代码在 PowerShell 中运行后,将给出下面显示的输出。

Script terminated when an error occurred

上面的输出表明脚本遇到了一个错误,运行了Catch块中的代码,然后终止了。

错误已被处理,这就是错误处理的目的。但是,显示的错误过于泛化。为了显示更详细的错误,您可以访问由Try块传递的错误的Exception属性。

下面的代码已经修改,特别是Catch块中的代码,以显示从当前错误传递的管道中获取的异常消息 – $PSItem.Exception.Message

$file_list = Get-Content .\filelist.txt
try {
    foreach ($file in $file_list) {
        Write-Output "Reading file $file"
        Get-Content $file -ErrorAction STOP
    }
}
catch {
    Write-Host $PSItem.Exception.Message -ForegroundColor RED
}
finally {
    $Error.Clear()
}

当运行上面修改后的代码时,显示的消息会更加详细。

Script terminated with a descriptive error message

捕获特定错误

有时,通用的错误处理并不是最合适的方法。也许,您希望脚本执行一个取决于遇到的错误类型的操作。

如何确定错误类型?通过检查上一个错误的Exception属性的TypeName值。例如,要查找前面示例中的错误类型,请使用以下命令:

$Error[0].Exception | Get-Member

上面代码的结果将会像下面的屏幕截图一样。正如您所见,TypeName值被显示为 – System.Management.Automation.ItemNotFoundException

Getting the error TypeName value

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

$file_list = Get-Content .\filelist.txt
try {
    foreach ($file in $file_list) {
        Write-Output "Reading file $file"
        Get-Content $file -ErrorAction STOP
    }
}
catch [System.Management.Automation.ItemNotFoundException]{
    Write-Host "The file $file is not found." -ForegroundColor RED
}
catch {
    Write-Host $PSItem.Exception.Message -ForegroundColor RED
}
finally {
    $Error.Clear()
}

下面的截图显示了上述修改后的代码的输出。

Script terminated with a specific error message

结论

在本文中,您了解了 PowerShell 中的错误、其属性以及如何确定错误的具体类型。您还了解了 `$ErrorActionPreference` 变量和 PowerShell `ErrorAction` 参数如何影响 PowerShell 处理非终止错误的方式。

您还学会了如何使用 PowerShell 的 `Try Catch Finally` 块执行错误处理,无论是针对特定错误还是通用方法。

本文中展示的示例仅演示了 `Try Catch Finally` 块的基本工作原理。我希望您在本文中获得的知识可以为您开始在脚本中应用错误处理提供起点。

进一步阅读

Source:
https://adamtheautomator.com/powershell-try-catch/