在使用 PowerShell 的過程當中,發現它的異常處理並不像想象中的那麼直觀,因此在這裏總結一下。數據庫
經過 ThrowTerminatingError 觸發的錯誤稱爲 Terminating Errors。本質上它是建立了一個異常,因此咱們可使用 catch 語句來捕獲 Terminating Errors。所以 Terminating Errors 的另一個名字叫 Exceptions。默認狀況下,Terminating Errors 不影響後面命令的執行!ui
把下面的代碼保存到文件 exception.ps1 中:spa
# 下面的命令不存在 Get-TerminatingError Write-Host 'hello world'
而後執行腳本 exception.ps1:日誌
注意最後輸出的 "hello world",雖然執行過程當中出現了錯誤,可是錯誤後面的代碼依然被執行了。
Terminating Errors 的特色是默認狀況下你能夠經過 catch 語句捕獲它,從而控制代碼的執行流。
把下面的代碼保存到文件 exception.ps1 中:code
Try{ # 下面的命令不存在 Get-TerminatingError } Catch{ Write-Host 'got you' exit 1 } Write-Host 'hello world'
而後執行腳本 exception.ps1:blog
這樣就好多了,咱們預測了代碼中可能產生的問題,而且在發生了錯誤的狀況下果斷的結束了腳本的執行。ip
雖然 Terminating Errors 不能中斷腳本的執行,可是卻能夠中斷 pipeline 的執行。從 Terminating Errors 的文檔中咱們能夠看到,其內部拋出了 PipelineStoppedException 異常,而且 PowerShell 內部處理了這個異常。因此正在執行的 pipeline 會被中斷掉,而後繼續執行後面的其它代碼。文檔
經過 Write-Error 觸發的錯誤稱爲 Non-Terminating Errors。本質上它只是把錯誤信息寫入了輸出流中而沒有產生異常,因此默認狀況下 catch 語句沒法捕獲 Non-Terminating Errors。默認狀況下,Non-Terminating Errors 不影響後面命令的執行!workflow
把下面的代碼保存到文件 exception.ps1 中:it
# C:\xxx 不存在 Copy-Item C:\xxx Write-Host 'hello world'
而後執行腳本 exception.ps1:
注意最後輸出的 "hello world",雖然執行過程當中出現了錯誤,可是錯誤後面的代碼依然被執行了。
Non-Terminating Errors 的特色是默認狀況下 catch 語句沒法捕獲它!
把下面的代碼保存到文件 exception.ps1 中:
Try{ # C:\xxx 不存在 Copy-Item C:\xxx } Catch{ Write-Host 'got you' exit 1 } Write-Host 'hello world'
而後執行腳本 exception.ps1,結果和上面是同樣的,錯誤後面的代碼依然會被執行。
Non-Terminating Errors 默認只是經過 Write-Error 把錯誤信息寫入了輸出流中而沒有產生異常,因此 catch 語句沒法捕獲 Non-Terminating Errors。可是咱們卻能夠經過 -ErrorAction 選項改變 WriteError 的默認行爲。
ErrorAction 選項主要用來改變命令的 non-terminating errors 的行爲(它不會對 Terminating Errors 產生影響)!
ErrorAction 選項的工做原理爲:用指定的參數覆蓋當前命令的 $ErrorActionPreference 變量。默認狀況下 $ErrorActionPreference 變量的值爲 Continue。
能夠爲 -ErrorAction 選項指定下面的參數:
-ErrorAction[:{Continue | Ignore | Inquire | SilentlyContinue | Stop | Suspend }]
它們表示的含義以下:
Continue 顯示錯誤信息並繼續執行後面的命令,這是默認值。
Ignore 這個值是在 PowerShell 3.0 引入的。它不顯示錯誤信息並繼續執行後面的命令。與 SilentlyContinue 不一樣的是,它也不會把錯誤信息添加到 $Error 變量中。
Inquire 顯示錯誤信息並彈框與用戶交互。
SilentlyContinue 不顯示錯誤信息並繼續執行後面的命令。
Stop 顯示錯誤信息而且退出腳本的執行。
Suspend 這個值只適用於 workflow。當 terminating error 發生時執行會暫停下來,而後決定是否恢復執行。
這裏咱們重點關注使用比較多的 stop, 它會讓 Write-Error 等本來產生 non-terminating error 的命令產生 terminating error!因此咱們就能夠用 catch 語句來捕獲異常了。把下面的代碼保存到文件 exception.ps1 中:
Try{ # C:\xxx 不存在 Copy-Item C:\xxx -ErrorAction Stop } Catch{ Write-Host 'got you' exit 1 } Write-Host 'hello world'
注意咱們爲 Copy-Item 命令添加了 -ErrorAction Stop 選項,而後執行腳本 exception.ps1:
哈哈,此次捕獲到異常了,而且最後也沒有輸出 "hello world"。
若是須要給每一個命令都添加 -ErrorAction 選項可不是什麼好玩的事情,好在咱們能夠在腳本中設置 $ErrorActionPreference 變量來完成一樣的功能:
$ErrorActionPreference = 'Stop'
這樣在當前的腳本中,全部本來的 non-terminating error 都會變成 terminating error。
在異常處理中不介紹 Try/Catch/Finally 語句是不完整的。Catch 語句用來捕獲 Try 塊中產生的異常,固然咱們能夠指定只捕獲那些咱們感興趣的異常。
Finally 塊也是很是重要的,它能保證一些必要的邏輯被執行,好比釋放數據庫鏈接。下面的 demo 演示瞭如何在腳本中設置 $ErrorActionPreference 變量:
$eap = $ErrorActionPreference Try{ $ErrorActionPreference = 'Stop' # do something } Catch{ Write-Host "error !" Exit 1 } Finally{ $ErrorActionPreference = $eap }
在 Finally 塊中把 $ErrorActionPreference 變量還原,保證咱們本身設置的 $ErrorActionPreference 變量隻影響 Try/Catch 塊中的語句。
$PSItem 是一個 ErrorRecord 類型的變量,它會保存異常的詳細信息。在捕獲到異常時,咱們能夠把異常相關的信息輸出或保存到日誌中:
$eap = $ErrorActionPreference Try{ $ErrorActionPreference = 'Stop' # 下面的命令不存在 Get-TerminatingError } Catch{ # 比較簡潔的信息 Write-Output $PSItem.ToString() } Finally{ $ErrorActionPreference = $eap }
$PSItem.ToString() 中只有錯誤的描述,看起來會比較簡潔。執行上面的腳本:
紅框中的信息就是 $PSItem.ToString() 提供的。
僅有錯誤信息並不老是可以很好的幫助調查問題的根源,若是有出錯的行號和異常堆棧會好不少:
$eap = $ErrorActionPreference Try{ $ErrorActionPreference = 'Stop' # 下面的命令不存在 Get-TerminatingError } Catch{ # 包含堆棧的信息 Write-Output $PSItem } Finally{ $ErrorActionPreference = $eap }
此次咱們直接輸出了 $PSItem,執行上面的代碼會獲得下面的輸出:
這下好多了,有了出錯的行號和調用堆棧就能夠很容易的看到出錯的緣由。
做爲一門腳本語言,PowerShell 對異常的支持仍是很是強大的。不過像 Terminating Errors 和 Non-Terminating Errors 這樣的特性也會給咱們帶來很多的困擾。但願本文介紹的內容能夠幫助你們更好的理解 PowerShell 中異常相關的概念。