簡短描述
說明 PowerShell 中錯誤的類型及其處理機制。
完整描述
PowerShell 區分三種錯誤類別:
- 非終止錯誤
- 語句終止錯誤
- 腳本終止錯誤
理解這些區別對於撰寫可靠的腳本和模組至關重要,因為每個類別有不同的預設行為,且需要不同的處理技巧。
此外,外部(原生)程式會透過退出代碼回報失敗,PowerShell 會獨立於自身錯誤系統追蹤這些代碼。
錯誤的類型
非終止錯誤
非終止錯誤會報告問題,但不會停止管線。 指令會繼續處理後續的輸入物件。 非終止錯誤由以下方式產生:
- Cmdlet
Write-Error - 進階函數中的方法
$PSCmdlet.WriteError() - 在個別輸入物件上遇到可復原故障的指令小子
預設情況下,PowerShell 會顯示錯誤訊息並繼續執行。
# Non-terminating error: the pipeline continues after the failure
'file1.txt', 'noSuchFile.txt', 'file3.txt' | ForEach-Object {
Get-Content $_ -ErrorAction Continue
}
在此範例中,報告 Get-Content 對 的 noSuchFile.txt 非終止錯誤,然後繼續處理 file3.txt。
非終止錯誤 不會 觸發 catch ,也 trap 不會預設觸發。
語句終止錯誤
語句終止錯誤會停止目前的語句(管線)執行,但執行會持續到腳本的下一個語句。 終止語句錯誤由以下方式產生:
- 進階函數與編譯指令小程式中的方法
$PSCmdlet.ThrowTerminatingError() - 引擎錯誤,例如
CommandNotFoundException(呼叫不存在的指令)和ParameterBindingException(參數參數無效) - .NET 方法呼叫拋出例外,例如
[int]::Parse('abc')
# Statement-terminating error: Get-Item fails, but the next statement runs
Get-Item -Path 'C:\NoSuchFile.txt'
Write-Output 'This still runs'
語句終止錯誤可由 try/catch 及 trap捕捉。
備註
.ThrowTerminatingError()
-ErrorAction不參考參數(除了Break輸入除錯器的值)。 然而,則 $ErrorActionPreference適用於 透過引擎語句層處理程序終止語句的錯誤。 例如,可以 $ErrorActionPreference = 'SilentlyContinue' 抑制語句終止錯誤,使腳本在下一句語句繼續。
-ErrorAction參數無法做到這點。 詳情請參見 $ErrorActionPreference不對稱性。
腳本終止錯誤
腳本終止錯誤會解開整個呼叫堆疊。 除非錯誤被區塊或trap語句捕捉try/catch,否則執行會完全停止。 腳本終止錯誤由以下方式產生:
-
throw關鍵詞 - 解析錯誤(導致腳本無法編譯的語法錯誤)
- 非終止錯誤由
-ErrorAction Stop$ErrorActionPreference = 'Stop'非進階情境中升級。 欲了解更多資訊,請參閱升級機制。 - 某些關鍵引擎故障
# Script-terminating error: throw unwinds the call stack
function Test-Throw {
throw 'Critical failure'
Write-Output 'This never runs'
}
Test-Throw
Write-Output 'This never runs either (unless caught)'
關鍵字 throw 預設會產生導致腳本終止的錯誤。 然而,當設定為 SilentlyContinue 或 Ignore時,$ErrorActionPreference可以抑制 throw 。 當呼叫一個進階函式時,參數 -ErrorAction SilentlyContinue會轉換成作用域局部 $ErrorActionPreference 值,因此在該函式內部也會抑制 throw 。
備註
即使有 $ErrorActionPreference = 'Ignore', throw 且被抑制的 仍會記錄 在 $Error中的一個項目。 這個 Ignore 值只會阻止 $Error 非 終止 錯誤的記錄。
這很重要
陳述終止與腳本終止這兩個術語描述的是影響範圍,而非錯誤的嚴重程度。 一個結束語句的錯誤會停止一個語句。 腳本終止錯誤會停止整個腳本及其呼叫者。 兩者皆可被 try/catch捕捉。
外部程式錯誤
外部(原生)程式不會直接參與 PowerShell 的錯誤系統。 它們會透過非零的退出碼來報告失敗,PowerShell 會將這個代碼儲存在自動變數中 $LASTEXITCODE 。
git clone https://example.com/nonexistent.git 2>$null
if ($LASTEXITCODE -ne 0) {
Write-Error "git failed with exit code $LASTEXITCODE"
}
預設情況下,原生程式的非零退出碼:
- 集合
$?為$false -
不會產生
ErrorRecord$Error -
不會觸發
catch或trap
PowerShell 7.3 新增了實驗偏好變數 ,該變數 $PSNativeCommandUseErrorActionPreference在 7.4 版本中成為穩定功能。 當你將此變數設為 $true時,會產生非零的退出代碼,發出 非終止錯誤 ,該錯誤訊息說明特定的退出代碼(a NativeCommandExitException)。 此錯誤尊重 $ErrorActionPreference,因此將其設定 Stop 為 ,將錯誤提升為可被捕捉 try/catch的終止腳本錯誤。
錯誤狀態變數
PowerShell 維護多個自動變數,反映當前錯誤狀態。
$?
包含 $true 若上一次操作成功且 $false 產生任何錯誤(非終止或終止)。 對於原生指令,是$?根據退出碼設定:$true對於退出碼0$false,否則。
Get-Item -Path 'C:\NoSuchFile.txt' 2>$null
$? # False
$Error
An ArrayList 儲存最新的錯誤紀錄,最近一次錯誤在索引 0中。 這份清單可以做到 $MaximumErrorCount (預設256個)。
所有終止錯誤都會加到 $Error。 對於終止錯誤,會Ignore抑制顯示,但仍記錄錯誤。$Error 所有非終止錯誤都會被加到 $Error 其他部分,除非 -ErrorAction Ignore 用於非終止錯誤,否則會同時阻止顯示和錄影。
$LASTEXITCODE
包含最後執行的原生程式的退出碼。 通常 的 0 值表示成功。 任何非零值表示失敗。 這個變數不會受到 PowerShell 指令檔錯誤的影響。
控制誤差行為
-ErrorAction共同參數
-ErrorAction共用參數會$ErrorActionPreference覆蓋單一指令。 它控制 PowerShell 對 該指令中非終止 錯誤的回應方式。
| 價值 | 行為 |
|---|---|
Continue |
顯示錯誤並繼續(預設) |
SilentlyContinue |
抑制顯示,新增, $Error繼續 |
Ignore |
抑制顯示,不要添加 $Error |
Stop |
升級為終止錯誤(參見升級流程) |
Inquire |
提示使用者做出決策 |
Break |
這時,除錯器登場了 |
-ErrorAction 不會改變由 所 $PSCmdlet.ThrowTerminatingError()產生錯誤的行為。 這些錯誤無論呼叫者偏好如何,都會終止語句。
$ErrorActionPreference 變數
$ErrorActionPreference偏好變數適用於目前範圍及子範圍中的所有指令。 它接受與相同的值 -ErrorAction。
$ErrorActionPreference = 'Stop'
# All non-terminating errors in this scope now become terminating
Write-Error 'This now throws' # Generates ActionPreferenceStopException
當 -ErrorAction 在指令中指定時,該指令的優先權會高於 $ErrorActionPreference 該指令。
升級流程
當 -ErrorAction Stop 或 $ErrorActionPreference = 'Stop' 生效時,PowerShell 會利用以下機制將非終止錯誤轉換為終止錯誤:
- 指令長在內部呼叫
WriteError()以發出非終止錯誤。 - 引擎會檢查指令的有效
ErrorAction偏好。 - 因為偏好值為
Stop,引擎會產生 ,ActionPreferenceStopException將原始錯誤記錄包裹起來。 - 若被
catch發現,原始錯誤資訊可透過$_.Exception.ErrorRecord存取。
升級錯誤的範圍取決於上下文:
- 在 非進階 腳本、函式或腳本區塊中,設定
$ErrorActionPreference = 'Stop'會升級為 導致腳本終止 的錯誤。 錯誤會向上傳播至呼叫堆疊。 - 在 進階 函式和腳本區塊(即 為
[CmdletBinding()]的)中,錯誤仍然是 陳述句終止。 執行會在呼叫後的下一個語句繼續執行。 - 傳遞
-ErrorAction Stop到進階函式的效果與設定$ErrorActionPreference = 'Stop'在其中相同,因為-ErrorAction轉換成一個作用域-局部$ErrorActionPreference值。
升級案例
非進階:腳本終止(「之後」不會列印)
& { param() $ErrorActionPreference = 'Stop' Get-Item 'NoSuchPath' } 2>$null 'after'進階:陳述結束(「之後」確實列印)
& { [CmdletBinding()] param() $ErrorActionPreference = 'Stop'; Get-Item 'NoSuchPath' } 2>$null 'after'若無
-ErrorAction Stop:非終止,則 catch 不會執行try { Write-Error 'This is non-terminating' Write-Output 'Execution continues' } catch { Write-Output "Caught: $_" # Not reached }與
-ErrorAction Stop:升級為終止try { Write-Error 'This becomes terminating' -ErrorAction Stop } catch { Write-Output "Caught: $_" # Reached }
升級錯誤可透過其原始例外類型被攔截。 引擎會展開 以 ActionPreferenceStopException 尋找底層例外:
try {
Get-Item -Path 'C:\NoSuchFile.txt' -ErrorAction Stop
} catch [System.Management.Automation.ItemNotFoundException] {
Write-Output "File not found: $($_.Exception.Message)"
}
不對稱性$ErrorActionPreference
-ErrorAction參數與$ErrorActionPreference變數在終端錯誤時行為不同。 理解這種不對稱性很重要:
-ErrorAction只影響 非終止 錯誤。 當指令長呼叫$PSCmdlet.ThrowTerminatingError()時,參數-ErrorAction會被忽略(除了Break,該參數會進入除錯器)。 錯誤總是會被拋出。$ErrorActionPreference影響非終止錯誤與陳述終止錯誤。 引擎的語句層級錯誤處理程式會$ErrorActionPreference讀取(而非-ErrorAction參數),當值為SilentlyContinue或Ignore時,可以抑制導致語句終止的錯誤。
function Test-Asymmetry {
[CmdletBinding()]
param()
$er = [System.Management.Automation.ErrorRecord]::new(
[System.InvalidOperationException]::new('test error'),
'TestError',
[System.Management.Automation.ErrorCategory]::InvalidOperation,
$null
)
$PSCmdlet.ThrowTerminatingError($er)
}
# -ErrorAction SilentlyContinue does NOT suppress the error:
Test-Asymmetry -ErrorAction SilentlyContinue # Error is still thrown
# $ErrorActionPreference DOES suppress the error:
$ErrorActionPreference = 'SilentlyContinue'
Test-Asymmetry # Error is silently suppressed, script continues
$ErrorActionPreference = 'Continue'
這很重要
$ErrorActionPreference 無法抑制設定 SuppressPromptInInterpreter 為 true的錯誤。 這些變數無論偏好變數如何都會傳播。 此類錯誤的例子包括:
-
ActionPreferenceStopException免於-ErrorAction Stop升級 - PowerShell 類別方法內部的錯誤
PipelineStoppedException
處理錯誤
try/catch/finally
用於 try/catch/finally 處理語句終止與腳本終止錯誤。 當區塊內部 try 發生錯誤時,PowerShell 會搜尋匹配 catch 的區塊。 無論是否發生錯誤,區 finally 塊都會執行。
try {
$result = Get-Content -Path 'data.txt' -ErrorAction Stop
}
catch [System.Management.Automation.ItemNotFoundException] {
Write-Warning 'Data file not found, using defaults.'
$result = 'default'
}
catch {
Write-Warning "Unexpected error: $_"
}
finally {
Write-Verbose 'Cleanup complete.' -Verbose
}
在區try塊內部,引擎會設定一個內部旗標,導致非終止性錯誤,這些錯誤由-ErrorAction Stop$ErrorActionPreference = 'Stop'區塊或傳播到該catch區塊。 這是設計好的行為,不是特殊情況。
完整語法細節請參見 about_Try_Catch_Finally。
trap
該 trap 敘述在作用域層級處理終止錯誤。 當包圍作用域中任何位置發生錯誤時,該 trap 區塊會執行。
-
預設 (no
break或continue):錯誤會顯示出來,執行會持續到造成錯誤的下一個陳述句。 -
continue陷阱中:抑制錯誤訊息,並在下一句恢復。 -
break在陷阱中:錯誤會傳播到父範圍。
trap [System.Management.Automation.CommandNotFoundException] {
Write-Warning "Command not found: $($_.TargetObject)"
continue
}
NonsenseCommand # Trap fires, execution continues
Write-Output 'This runs because the trap used continue'
完整語法細節請參見 about_Trap。
函式與腳本錯誤的回報
撰寫函式與腳本時,選擇與失敗嚴重程度相符的錯誤回報機制。
非終止性 - 使用 Write-Error
當函式能繼續處理其他輸入時使用 Write-Error 。 這適用於處理多個物件並在個別項目上遇到故障的管線功能。
function Test-Path-Safe {
[CmdletBinding()]
param([Parameter(ValueFromPipeline)][string]$Path)
process {
if (-not (Test-Path $Path)) {
Write-Error "Path not found: $Path"
return
}
$Path
}
}
備註
在進階函式(帶有 [CmdletBinding()]的函式)中,請使用 $PSCmdlet.WriteError() 代替 ,Write-Error以確保 在$?呼叫者的作用域中正確設定為 。$false
Write-Error指令檔不一定每次都能正確設定$?。
語句終止 - 使用 $PSCmdlet.ThrowTerminatingError()
當函式無法繼續,但呼叫者應該決定如何處理失敗時才會使用 $PSCmdlet.ThrowTerminatingError() 。 這是進階函數中推薦的做法。
function Get-Config {
[CmdletBinding()]
param([string]$Path)
if (-not (Test-Path $Path)) {
$er = [System.Management.Automation.ErrorRecord]::new(
[System.IO.FileNotFoundException]::new("Config file not found: $Path"),
'ConfigNotFound',
[System.Management.Automation.ErrorCategory]::ObjectNotFound,
$Path
)
$PSCmdlet.ThrowTerminatingError($er)
}
Get-Content $Path | ConvertFrom-Json
}
錯誤離開函式後,呼叫者預設將其視為非終止錯誤。 來電者可以用 來升級。-ErrorAction Stop
腳本終止 - 使用 throw
當無法恢復且整個劇本應該停止時使用 throw 。
$config = Get-Content 'config.json' -ErrorAction SilentlyContinue |
ConvertFrom-Json
if (-not $config) {
throw 'Cannot proceed without a valid configuration file.'
}
該使用哪種機制
- 當處理多個輸入且部分可能失敗時,請使用
Write-Error或$PSCmdlet.WriteError()。 - 如果函式無法繼續,就使用
$PSCmdlet.ThrowTerminatingError()並讓呼叫者決定如何處理。 - 如果整個腳本必須立即停止,請使用
throw。
錯誤類型摘要
以下表格總結了 PowerShell 中不同錯誤類型的屬性與行為。
非終止誤差
非終止錯誤可由或Write-Error$PSCmdlet.WriteError()產生。
| Attribute | 說明 |
|---|---|
| 影響範圍 | 管線持續進行 |
被抓到 catch |
沒有(除非被升級) |
被抓到 trap |
沒有(除非被升級) |
新增內容 $Error |
是的(除非 Ignore) |
集合 $? 為 $false |
是的 |
受影響者 -ErrorAction |
是的 |
受影響者 $ErrorActionPreference |
是的 |
語句終止錯誤
終止語句錯誤 可能由 ThrowTerminatingError()引擎錯誤、.NET 方法例外或 -ErrorAction Stop 進階情境中產生。
| Attribute | 說明 |
|---|---|
| 影響範圍 | 當前陳述停止;劇本繼續 |
被抓到 catch |
是的 |
被抓到 trap |
是的 |
新增內容 $Error |
是的 |
集合 $? 為 $false |
是的 |
受影響者 -ErrorAction |
沒有(Break 只有) |
受影響者 $ErrorActionPreference |
是的(可以壓制) |
腳本終止錯誤
腳本終止錯誤 可由 throw、解析錯誤 -ErrorAction Stop 或非進階上下文產生。
| Attribute | 說明 |
|---|---|
| 影響範圍 | 買權堆疊會回轉 |
被抓到 catch |
是的 |
被抓到 trap |
是的 |
新增內容 $Error |
是的 |
集合 $? 為 $false |
是的 |
受影響者 -ErrorAction |
No |
受影響者 $ErrorActionPreference |
throw:是的(可以抑制) |
受影響者 $ErrorActionPreference |
升級:視情境而定 |