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

错误可能会变得压倒性和令人困惑。最重要的是,错误通常很难阅读,这使得确定脚本在哪里出错几乎不可能。
幸运的是,在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有一個稱為參考變量的概念。這些變量用於在許多不同的方面改變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
值。
讓我們回到上個例子中使用相同的代碼。但是,這次在Get-Content
行中添加了ErrorAction
參數。
運行修改後的代碼後,您將看到即使$ErrorActionPreference
設置為Continue
,腳本仍然在遇到錯誤時終止。腳本終止是因為PowerShell ErrorAction
參數值在Get-Content
中設置為STOP
。

ErrorAction
parameter使用PowerShell的Try Catch區塊
此時,您已經了解了PowerShell錯誤以及$ErrorActionPreference
變量和PowerShell ErrorAction
參數的工作原理。現在,是時候學習有關重要內容的知識了- PowerShell的Try Catch Finally
區塊。
PowerShell的try catch
區塊(以及可選的finally block
)是一種將程式碼包裹起來,並捕獲任何返回的錯誤的方法。
下面的程式碼顯示了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
。

既然你已經知道需要攔截的錯誤類型,現在修改代碼以專門捕獲它。如下所示的修改後的代碼中,現在有兩個
下面的截圖顯示了上述修改後的代碼的輸出。

結論
在本文中,你學習了PowerShell中的錯誤、它的屬性以及如何確定錯誤的具體類型。你還學會了$ErrorActionPreference變量和PowerShell ErrorAction參數在PowerShell如何處理非終止錯誤方面的區別。
你還學會了如何使用PowerShell的Try Catch Finally塊進行錯誤處理,無論是針對特定錯誤還是一個通用的方法。
本文示例只演示了Try Catch Finally塊的基本工作原理。我希望你在本文中獲得的知識能讓你開始在你的腳本中應用錯誤處理。