HOWTO · PowerShell
如何在 Windows PowerShell 中終止腳本
瞭解何時使用 exit、return、throw、break、continue 或 Stop-Process,終止正確的範圍並傳回可靠的 PowerShell 結束代碼。
本頁內容
終止 PowerShell 腳本並不是單一操作。你需要先確認要停止的是整個腳本處理程序、目前函式、一次迴圈反覆項目,還是另一個作業系統處理程序,再選擇對應的陳述式。
| 目的 | 使用方式 |
|---|---|
| 結束最上層腳本並回報狀態 | exit <代碼> |
| 離開函式、腳本或腳本區塊 | return |
| 回報可由呼叫端攔截的錯誤 | throw |
離開迴圈或 switch |
break |
跳到下一個迴圈或 switch 項目 |
continue |
| 終止另一個本機處理程序 | Stop-Process |
使用 exit 終止 PowerShell 腳本
exit 會結束腳本或 PowerShell 執行個體。選擇性的整數參數會成為處理程序結束代碼。依照慣例,0 表示成功,非零值表示失敗。請記錄每個錯誤代碼的意義,讓排程工作、CI 作業及包裝程式能正確處理結果。
將下列進入點腳本儲存為 Check-Config.ps1:
param([Parameter(Mandatory)][string]$Path)
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
Write-Error "Configuration file not found: $Path"
exit 2
}
Write-Output 'Configuration file found.'
exit 0
從另一個 PowerShell 工作階段將它當作子處理程序執行,再讀取狀態:
powershell.exe -NoProfile -File .\Check-Config.ps1 -Path .\missing.json
$LASTEXITCODE
最後一個命令會輸出 2。在 PowerShell 7 中,請使用 pwsh 取代 powershell.exe。PowerShell 呼叫端透過 $LASTEXITCODE 讀取子處理程序狀態;cmd.exe 呼叫端則讀取 %ERRORLEVEL%。
最好只在最上層腳本邊界使用 exit。如果可重複使用的函式呼叫 exit,它可能關閉互動式工作階段、測試執行器或其他呼叫它的主機。函式應傳回資料或擲回錯誤,僅由進入點腳本將結果轉換成結束代碼。
使用 return 離開目前範圍
return 會離開目前的函式、腳本或腳本區塊,但它本身不會設定處理程序結束代碼。
function Get-ConfigText {
param([string]$Path)
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
return $null
}
Get-Content -LiteralPath $Path -Raw
}
在一般 PowerShell 函式中,所有未擷取的成功資料流值都會成為輸出,不只是 return 後面的運算式。診斷訊息應寫入 Write-Verbose 等適當的資料流,不要用 Write-Output 發出額外字串,以免改變函式結果。
使用 throw 回報可攔截的錯誤
throw 預設會產生終止腳本的錯誤,並回溯呼叫堆疊,直到 catch 區塊或 trap 處理它。當函式無法產生有效結果,且應由呼叫端決定如何復原時,可以使用它。
function Get-RequiredConfig {
param([string]$Path)
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
throw "Configuration file not found: $Path"
}
Get-Content -LiteralPath $Path -Raw
}
try {
$config = Get-RequiredConfig -Path '.\settings.json'
}
catch {
Write-Error $_
exit 2
}
這個模式分開了兩項責任:函式透過 throw 說明失敗原因,進入點腳本攔截錯誤並選擇對外的結束代碼。並非所有 Cmdlet 錯誤都會自動進入 catch;若需要在其中處理非終止錯誤,請為相應命令加入 -ErrorAction Stop。
僅在控制區塊中使用 break 和 continue
break 會離開最近的迴圈或 switch。continue 會略過目前反覆項目的其餘部分,並開始下一次反覆運算。
foreach ($name in 'alpha', '', 'beta', 'stop', 'omega') {
if ([string]::IsNullOrWhiteSpace($name)) {
continue
}
if ($name -eq 'stop') {
break
}
Write-Output $name
}
輸出:
alpha
beta
它們不是通用的腳本終止命令。在迴圈、switch 或 trap 之外使用時,PowerShell 可能沿著呼叫堆疊尋找外層控制結構;如果找不到,目前的 Runspace 可能會被終止。
使用 Stop-Process 終止另一個處理程序
Stop-Process 的目標是本機電腦上的另一個處理程序,而不是停止目前腳本。使用寬泛條件前,先以 -WhatIf 預覽目標:
Get-Process -Name notepad -ErrorAction SilentlyContinue |
Stop-Process -WhatIf
確認目標之後再移除 -WhatIf。若一個名稱可能符合多個處理程序,請優先使用已知的處理程序物件或 PID。終止其他使用者擁有的處理程序,也可能需要以系統管理員身分執行 PowerShell。
保留原生程式的結束代碼
$? 表示上一個 PowerShell 命令是否成功,而 $LASTEXITCODE 儲存最近一個原生程式的結束代碼。下一個原生命令可能覆寫該值,因此應立即儲存。
git status --porcelain
$gitCode = $LASTEXITCODE
if ($gitCode -ne 0) {
Write-Error "git failed with exit code $gitCode"
exit $gitCode
}
在可重複使用的邏輯中使用 return 或 throw,並將 exit <代碼> 保留在最上層腳本邊界。如此可清楚區分函式輸出、控制流程與作業系統處理程序狀態。
確保清理並區分取消作業
請將清理工作放在 finally 中,不要放在可能終止處理的行之後。根據 PowerShell 語言關鍵字文件,無論 try 中的工作成功、錯誤到達 catch、呼叫 exit,或 Ctrl+C 中斷腳本,finally 都會執行,因此適合釋放資料流、鎖定或暫存檔。
$stream = $null
try {
$stream = [System.IO.File]::OpenRead($Path)
# Process the stream.
}
finally {
if ($null -ne $stream) {
$stream.Dispose()
}
}
請讓可重複使用的程式碼以 throw 或 return 回到進入點腳本,而不要在處理中間呼叫 exit。這樣進入點腳本能在明確的位置清理、寫入診斷並選擇處理程序狀態。Ctrl+C、被停止的工作和無效輸入是不同事件:請決定並記錄每一種是取消、驗證失敗,還是呼叫端或排程器必須處理的其他狀態。不要悄悄把它們歸為同一種通用失敗。終止另一個處理程序又是另一回事;應優先使用應用程式的正常關閉方式。Stop-Process -Force 可能阻止目標清理檔案或狀態,只有理解此後果時才使用。
讓子腳本和主機處於預期範圍
呼叫運算子會在子腳本自己的腳本範圍中執行它;點來源會在目前範圍中執行腳本並匯入其函式和變數。僅在有意匯入定義時使用點來源。
# Run the child in its own script scope.
& .\Child.ps1
# Import definitions into the current scope.
. .\Functions.ps1
可重複使用程式碼中的 exit 比 return 更強,它可能關閉互動式工作階段、測試執行器或其他主機。以 powershell.exe -File 或 pwsh -File 啟動的子處理程序會把控制權交還呼叫端;互動式主控台可能關閉目前工作階段。內嵌 Runspace 和編輯器的行為可能不同。請分別測試實際生產命令列中的 -File 與 -Command、設定檔、引號、輸出和結束狀態。
建議
只在刻意的最上層邊界使用 exit <代碼>,以 return 離開目前範圍,並用 throw 回報呼叫端可處理的失敗。只在可見的迴圈或 switch 中使用 break 和 continue,而且僅用 Stop-Process 終止獨立的本機處理程序。傳回資料或擲回錯誤的函式可保持重複使用;小型進入點腳本可將結果轉為 shell、排程器、CI 或包裝程式可用的穩定狀態。