PowerShell 異常處理


在使用 PowerShell 的過程中,發現它的異常處理並不像想象中的那么直觀,所以在這里總結一下。

Terminating Errors

通過 ThrowTerminatingError 觸發的錯誤稱為 Terminating Errors。本質上它是創建了一個異常,所以我們可以使用 catch 語句來捕獲 Terminating Errors。因此 Terminating Errors 的另外一個名字叫 Exceptions。默認情況下,Terminating Errors 不影響后面命令的執行!

把下面的代碼保存到文件 exception.ps1 中:

# 下面的命令不存在
Get-TerminatingError

Write-Host 'hello world'

然后執行腳本 exception.ps1:

注意最后輸出的 "hello world",雖然執行過程中出現了錯誤,但是錯誤后面的代碼依然被執行了。
Terminating Errors 的特點是默認情況下你可以通過 catch 語句捕獲它,從而控制代碼的執行流。
把下面的代碼保存到文件 exception.ps1 中:

Try{
    # 下面的命令不存在
    Get-TerminatingError
}
Catch{
    Write-Host 'got you'
    exit 1
}
Write-Host 'hello world'

然后執行腳本 exception.ps1:

這樣就好多了,我們預測了代碼中可能產生的問題,並且在發生了錯誤的情況下果斷的結束了腳本的執行。

雖然 Terminating Errors 不能中斷腳本的執行,但是卻可以中斷 pipeline 的執行。從 Terminating Errors 的文檔中我們可以看到,其內部拋出了 PipelineStoppedException 異常,並且 PowerShell 內部處理了這個異常。所以正在執行的 pipeline 會被中斷掉,然后繼續執行后面的其它代碼。

Non-Terminating Errors

通過 Write-Error 觸發的錯誤稱為 Non-Terminating Errors。本質上它只是把錯誤信息寫入了輸出流中而沒有產生異常,所以默認情況下 catch 語句無法捕獲 Non-Terminating Errors。默認情況下,Non-Terminating Errors 不影響后面命令的執行!

把下面的代碼保存到文件 exception.ps1 中:

# 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,結果和上面是一樣的,錯誤后面的代碼依然會被執行。

ErrorAction 選項的秘密

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

在異常處理中不介紹 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 變量

$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 中異常相關的概念。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM