about_Error_Handling

簡短描述

說明 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/catchtrap捕捉。

備註

.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 預設會產生導致腳本終止的錯誤。 然而,當設定為 SilentlyContinueIgnore$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
  • 不會觸發catchtrap

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 會利用以下機制將非終止錯誤轉換為終止錯誤:

  1. 指令長在內部呼叫 WriteError() 以發出非終止錯誤。
  2. 引擎會檢查指令的有效 ErrorAction 偏好。
  3. 因為偏好值為 Stop,引擎會產生 , ActionPreferenceStopException 將原始錯誤記錄包裹起來。
  4. 若被 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 參數),當值為 SilentlyContinueIgnore時,可以抑制導致語句終止的錯誤。

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 無法抑制設定 SuppressPromptInInterpretertrue的錯誤。 這些變數無論偏好變數如何都會傳播。 此類錯誤的例子包括:

  • 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 breakcontinue):錯誤會顯示出來,執行會持續到造成錯誤的下一個陳述句。
  • 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 升級:視情境而定

另請參閱