掌握 PowerShell 的 Try Catch 塊,附有示例

您是否曾运行过脚本或PowerShell cmdlet,并遇到以下显示的大段红色文本,让您感到困惑和不知所措?

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有一個稱為參考變量的概念。這些變量用於在許多不同的方面改變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值。

讓我們回到上個例子中使用相同的代碼。但是,這次在Get-Content行中添加了ErrorAction參數。

# 將$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,腳本仍然在遇到錯誤時終止。腳本終止是因為PowerShell ErrorAction參數值在Get-Content中設置為STOP

Forcing a terminating error using the PowerShell ErrorAction parameter

使用PowerShell的Try Catch區塊

此時,您已經了解了PowerShell錯誤以及$ErrorActionPreference變量和PowerShell ErrorAction參數的工作原理。現在,是時候學習有關重要內容的知識了- PowerShell的Try Catch Finally區塊。

PowerShell的try catch區塊(以及可選的finally block)是一種將程式碼包裹起來,並捕獲任何返回的錯誤的方法。

下面的程式碼顯示了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

既然你已經知道需要攔截的錯誤類型,現在修改代碼以專門捕獲它。如下所示的修改後的代碼中,現在有兩個塊。第一個塊攔截特定類型的錯誤(System.Management.Automation.ItemNotFoundException)。相比之下,第二個塊包含通用的、捕獲所有錯誤的消息。

$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/