錯誤處理

已完成

到目前為止,您已瞭解如何新增參數和流量控制建構,讓您的指令碼有彈性且能更安全地使用。 但有時候您會在指令碼中收到錯誤。 您需要有方式來處理這些錯誤。

需要考慮的因素有兩項:

  • 如何處理錯誤。 有時候您會收到可以復原的錯誤,有時候最好是停止指令碼。 請務必考慮可能發生的錯誤類型,以及如何妥善管理。

  • 錯誤有多嚴重。 有各種不同類型的錯誤訊息。 有些是使用者覺得不正常的警告。 有些則是更嚴重的,且使用者真的需要注意。 您的錯誤處理方法取決於錯誤的類型。 這種方法可能是來自呈現訊息來提高嚴重性層級,並可能會停止指令碼的任何事項。

錯誤

例如,Cmdlet 或函式可能會產生許多類型的錯誤。 我們建議您撰寫程式碼來管理每個可能發生的錯誤類型,並在指定類型時適當地進行管理。 例如,假設您正在嘗試寫入檔案。 根據錯誤的不同,您可能會遇到各種類型的錯誤。 如果您不允許寫入檔案,您可能會收到一種類型的錯誤。 如果檔案不存在,您可能會收到另一種類型的錯誤,依此類推。

當您執行 PowerShell 時,可以取得兩種類型的錯誤:

  • 終止錯誤。 此類型的錯誤將會在發生錯誤的資料列上停止執行。 您可使用 Try-CatchTrap 來處理這類型的錯誤。 如果未處理錯誤,指令碼將會在該點結束,且不會執行任何陳述式。

    注意

    Trap 建構在此課程模組範圍之外。 如果您有興趣,請參閱 關於陷阱

  • 非終止錯誤。 這種類型的錯誤會通知使用者發生問題,但指令碼會繼續。 您可以將此類型的錯誤升級為終止錯誤。

使用 Try/Catch/Finally 管理錯誤

您可以將終止錯誤視為未預期的錯誤。 這些錯誤很嚴重。 當您處理其中一種情況時,您應該考慮是何種類型的錯誤,以及該怎麼處理。

有三個相關的建構可協助您管理這類型的錯誤:

  • Try。 您會使用 Try 區塊來封裝一或多個陳述式。 您會將想要執行的程式碼 (例如,寫入資料來源的程式碼) 放在大括弧內。 Try 至少必須有一個 CatchFinally 區塊。 其外觀如下:

    Try {
       # Statement. For example, call a command.
       # Another statement. For example, assign a variable.
    }
    
  • Catch。 當發生錯誤時,您會使用此關鍵字來「攔截」或「管理」錯誤。 然後,您會檢查例外狀況物件,以瞭解發生了哪種類型的錯誤、發生的位置,以及指令碼是否可以復原。 Catch 緊接在 Try 之後。 如果您想要的話,可以包含一個以上的 Catch (每種類型的錯誤各一個)。 以下為範例:

    Try {
       # Do something with a file.
    } Catch [System.IO.IOException] {
       Write-Host "Something went wrong"
    }  Catch {
       # Catch all. It's not an IOException but something else.
    }
    

    指令碼會嘗試執行執行一些 I/O 工作的命令。 第一個會 Catch 攔截特定類型的錯誤:[System.IO.IOException]。 最後一個 Catch 會攔截任何不是 [System.IO.IOException] 的錯誤。

  • Finally。 不論是否發生任何錯誤,都會執行此區塊中的陳述式。 您可能不會使用這種區塊,但這對清除資源很有用。 若要使用,請將其新增為最後一個區塊:

    Try {
       # Do something with a file.
    } Catch [System.IO.IOException] {
       Write-Host "Something went wrong"
    }  Catch {
       # Catch all. It's not an IOException but something else.
    } Finally {
       # Clean up resources.
    }
    

檢查錯誤

我們已在攔截錯誤的內容中討論過例外狀況物件。 您可使用這些物件來檢查發生錯誤的原因,並採取適當的措施。 例外狀況物件包含:

  • 訊息。 此訊息會簡短告訴您發生錯誤的地方。

  • 堆疊追蹤。 堆疊追蹤會告知您在錯誤之前執行的陳述式。 假設您呼叫了函式 A、後面接著 B,然後是 C。指令碼會停止回應 C。堆疊追蹤將會顯示該呼叫鏈。

  • 違規的資料列。 例外狀況物件也會告訴您當錯誤發生時,指令碼正在執行的資料列。 此資訊可協助您偵錯您的程式碼。

那麼,您要如何檢查例外狀況物件呢? 有一個具有 exception 屬性的內建變數 $_。 例如,若要取得錯誤訊息,請使用 $_.exception.message。 在程式碼中,可能如下所示:

Try {
     # Do something with a file.
   } Catch [System.IO.IOException] {
     Write-Host "Something IO went wrong: $($_.exception.message)"
   }  Catch {
     Write-Host "Something else went wrong: $($_.exception.message)"
   }

引發錯誤

在某些情況下,建議您產生錯誤:

  • 非終止錯誤。 例如,針對此類型的錯誤,PowerShell 只會使用 Write-Error Cmdlet 通知您發生錯誤。 指令碼繼續執行。 這可能不是您想要的行為。 若要提高錯誤的嚴重性,您可以使用像這樣的參數 -ErrorAction 來引發可攔截的錯誤 Try/Catch,如下所示:

    Try {
       Get-Content './file.txt' -ErrorAction Stop
    } Catch {
       Write-Error "File can't be found"
    }
    

    使用 -ErrorAction 參數和值 Stop,您可能會造成 Try/Catch可能攔截到的錯誤。

  • 商務規則。 可能會有一種情況,那就是程式碼不會實際停止回應,但您會基於商務原因而希望如此。 假設您正在處理輸入,並檢查參數是否為路徑。 商務需求可能是只允許特定的路徑,或路徑需要以某種方式來查看。 如果檢查失敗,則會「擲回」錯誤。 在這種情況下,您可以使用 Throw 區塊:

    Try {
       If ($Path -eq './forbidden') 
       {
         Throw "Path not allowed"
       }
       # Carry on.
    
    } Catch {
       Write-Error "$($_.exception.message)" # Path not allowed.
    }
    
    

    注意

    一般情況下,請不要使用 Throw 進行參數驗證。 請改用 驗證屬性 。 如果您無法讓程式碼使用這些屬性,Throw 可能 OK。