HOWTO · PowerShell

如何在 Windows PowerShell 中終止腳本

本文將列舉幾種在 Windows PowerShell 中終止腳本的方法。本文將解釋並定義所有命令,並引用範例腳本區塊來說明它們的適用場景。

停止 PowerShell 腳本:選擇正確的語句

使用 exit 當腳本執行完畢並必須向呼叫者傳回退出狀態時,請勿使用。 breakcontinue, 或者 return 停止腳本的方法可以互換:每種方法控制不同的作用域。若要停止一個獨立的作業系統進程,請使用 Stop-Process, 不是 exit

這種區別在計劃任務、持續整合作業以及由其他進程呼叫的腳本中至關重要。清晰的退出狀態可以讓呼叫者判斷操作是否成功。清晰的錯誤訊息可以讓呼叫者處理失敗,而無需默默地繼續執行。

使用以下命令結束腳本並設定退出程式碼 exit

exit 停止目前腳本或 PowerShell 實例並進行設置 $LASTEXITCODE。 使用 0 成功表示傳回一個記錄在案的非零整數,失敗則傳回一個記錄在案的非零整數。在由…啟動的腳本中 pwsh -File 或者 powershell.exe -File, 預訂 exit 腳本處理完預期錯誤後的最終狀態。

if (-not (Test-Path -LiteralPath $Path)) {
    Write-Error "Missing file: $Path"
    exit 2
}

Write-Output 'Processing completed.'
exit 0

聲明之後 exit 無法運行。在 Windows 系統中,退出代碼可以是任何有符號的 32 位元整數。在類 Unix 主機上,為了確保 shell 行為的可移植性,請使用正位元組大小的退出代碼。不要依賴於此。 $LASTEXITCODE 描述 cmdlet 失敗:主要用於描述本機執行檔的退出程式碼和 exit 陳述。

僅保留當前函數或腳本區塊 return

return 退出目前作用域,該作用域可以是函數、腳本或腳本區塊。它可以向輸出流寫入一個可選值。在普通的 PowerShell 函數中,任何未捕獲的輸出也會被傳回,因此請避免這樣做。 Write-Output 用於不應成為功能結果的診斷資訊。

function Get-ConfigurationValue {
    param([string]$Path)

    if (-not (Test-Path -LiteralPath $Path)) {
        return $null
    }

    return Get-Content -LiteralPath $Path -Raw
}

$value = Get-ConfigurationValue -Path './settings.json'

使用 return 用於函數中的早期結果或守衛子句。當外部呼叫者需要進程退出程式碼時,請勿使用此方法;讓頂層腳本決定是否呼叫。 exit

使用有意義的錯誤停止處理 throw

throw 會產生一個導致腳本終止的錯誤。除非另有說明,否則它會回滾呼叫堆疊。 try/catch 或者 trap 它會處理這種情況。當繼續執行操作不安全,或呼叫者需要知道失敗原因時,請使用它。

try {
    $content = Get-Content -LiteralPath $Path -ErrorAction Stop
}
catch {
    throw "Cannot read '$Path': $($_.Exception.Message)"
}

一個 catch 程式碼區塊可以記錄上下文,並進行清理。 finally或重新拋出原始錯誤 throw避免將每個非零的本機程式退出代碼都視為捕獲的 PowerShell 例外:本機命令集 $LASTEXITCODE 通常不會觸發 catch 沒有明確錯誤處理配置。

使用 breakcontinue 僅適用於控制塊

break 退出最近的循環或 switchcontinue 跳到下一個循環或 switch 迭代。它們不是通用的腳本終止命令。

foreach ($name in $names) {
    if ([string]::IsNullOrWhiteSpace($name)) {
        continue
    }
    if ($name -eq 'stop') {
        break
    }
    Write-Output $name
}

在迴圈、switchtrap 之外使用這些關鍵字時,PowerShell 會沿呼叫堆疊尋找外層控制區塊,並可能意外終止目前的執行空間。不要在 ForEach-Object 等管道指令碼區塊中使用它們來表示一般的指令碼退出。

使用以下命令停止另一個進程 Stop-Process

腳本和作業系統進程是不同的概念。 Stop-Process 它會停止目標進程,但不會直接結束目前腳本。請謹慎選擇進程,尤其是在進程名稱可能符合多個進程的情況下。

$process = Get-Process -Name notepad -ErrorAction SilentlyContinue
if ($process) {
    Stop-Process -InputObject $process
}

使用 -WhatIf 首先是破壞性自動化,並且優先選擇明確的進程物件或進程 ID,而不是廣泛的進程名稱匹配。

概括

使用 exit <code> 在腳本邊界處, return 就目前範圍而言, throw 對於終止故障,以及 break 或者 continue 僅在受支援的控制區塊內使用。 Stop-Process 只有當任務是終止另一個進程時才需要這樣做。這種分離使得 PowerShell 自動化對使用者和呼叫工具而言都具有可預測性。

向呼叫進程返回可靠狀態

自動化呼叫程式通常會觀察進程退出程式碼、標準輸出和標準錯誤。請精心設計這些通道。將正常結果寫入成功輸出流,將診斷資訊寫入對應的資訊流或錯誤流,並在頂層腳本末尾添加文件化的程式碼。

param([Parameter(Mandatory)][string]$Path)

try {
    Get-Content -LiteralPath $Path -ErrorAction Stop |
        Measure-Object -Line
    exit 0
}
catch [System.Management.Automation.ItemNotFoundException] {
    Write-Error "Input file was not found: $Path"
    exit 2
}
catch {
    Write-Error $_
    exit 1
}

呼叫 PowerShell 的 shell 可以在 PowerShell 退出後檢查其狀態。從另一個 PowerShell 進程中,該值是可用的。 $LASTEXITCODECI 系統和調度器也使用它來標記步驟成功或失敗。請在腳本文件中定義程式碼意義,而不是讓呼叫者從訊息中推斷。

理解 $?$LASTEXITCODE

$? 報告最後一個 PowerShell 指令是否成功執行。 $LASTEXITCODE 儲存最後一個本機程式的退出代碼或明確退出代碼。 exit 從呼叫腳本上下文中取得。它們相關但不可互換。

Get-Item -LiteralPath './missing.txt' -ErrorAction SilentlyContinue
Write-Output "Cmdlet succeeded: $?"

git status --porcelain
Write-Output "Native exit code: $LASTEXITCODE"

本機可以傳回非零狀態,而不會產生終止 PowerShell 錯誤。 catch 預期。檢查 $LASTEXITCODE或者,當支援的 PowerShell 版本和腳本策略要求時,使用相應的現代原生命令錯誤首選項。反之,cmdlet 錯誤不一定會分配有意義的新原生退出程式碼。

呼叫另一個腳本而不意外結束主機

呼叫方式會改變控制語句的作用域。 call 運算子在其自身的腳本作用域中執行腳本。點源呼叫則在目前作用域中執行腳本,並匯入其函數和變數。 exit 聲明比 return 並可依執行的啟動方式結束宿主 PowerShell 程序。

# Run in its own script scope.
& './Child.ps1'

# Run in the current scope. Use only when importing definitions is intended.
. './Functions.ps1'

可重複使用的子腳本通常應該報告物件或拋出錯誤,並讓頂層入口腳本選擇最終的退出代碼。這樣可以防止輔助腳本意外關閉互動式 shell、測試執行器或父自動化宿主程式。

使用 return 仔細閱讀管道和函數

PowerShell 會將函數中所有未捕獲的成功輸出寫入其結果流,而不僅僅是表達式後面的部分。 return因此,診斷字串可能會改變表面的回傳值。

function Get-Port {
    Write-Verbose 'Reading configured port'
    return 8080
}

$port = Get-Port -Verbose

Write-Verbose 使用詳細輸出流,因此 $port 仍然是預期的整數。純字串表達式或 Write-Outputreturn 也將成為其中的一部分 $port。 使用 return 主要目的是儘早離開當前範圍;在返回資料時有意識地管理資料流。

保護清理工作 finally

當腳本開啟資源時,使用 finally 對於無論操作成功或拋出例外都必須執行的清理操作,這樣做比僅將清理操作放在可能提前終止的程式碼之後更安全。

$stream = $null

try {
    $stream = [System.IO.File]::OpenRead($Path)
    # Process the stream.
}
catch {
    throw
}
finally {
    if ($null -ne $stream) {
        $stream.Dispose()
    }
}

避免打電話 exit 深處 try 僅用於表達失敗。拋出異常或返回入口點,以便腳本的清理和狀態策略保持可見且可測試。

將取消操作與失敗操作分開處理

使用者按下 Ctrl+C、作業停止以及腳本偵測到無效輸入是不同的事件。不要強制所有情況都使用相同的退出代碼。對於非互動式自動化,請記錄哪些程式碼表示驗證失敗、資源不可用或外部命令失敗。對於互動式工具,請提供足夠的上下文信息,以便用戶決定下一步。

Stop-Process -Force 並非通用的取消機制。它可能會阻止目標程式清理檔案或狀態,並且停止受保護的進程可能需要更高的權限。如果目標應用程式有正常的關閉機制,請優先使用。僅在充分了解其後果的情況下才使用強制終止程序。

從預期邊界中選擇陳述

詢問應該在哪個邊界處停止。若要停止一次循環迭代,請使用 continue離開一個循環或 switch, 使用 break若要離開目前函數、腳本或腳本區塊,請使用 return若要報告來電者可能發現的故障,請使用 throw若要使用狀態結束頂層腳本或 PowerShell 主機,請使用 exit若要終止其他作業系統進程,請使用 Stop-Process

保持這些邊界的分離可以改進測試。傳回或拋出異常的函數可以被單元測試調用,而無需關閉測試進程。一個簡潔的入口腳本可以將結果轉換為 shell、排程器或部署系統可用的穩定退出程式碼。

PowerShell 主機帳戶

可見效果 exit 這取決於 PowerShell 的託管方式。在互動式控制台中,它可以關閉目前的 PowerShell 會話。在作為子進程啟動的腳本中,它會結束該子進程並將控制權傳回給父進程。在嵌入式運行空間或編輯器中,宿主行為可能有所不同,這也是可重複使用函數應該傳回或拋出異常而不是呼叫的另一個原因。 exit 他們自己。

cmd.exe無論是 Bash、Python 還是建置運行器,都要測試生產環境中實際使用的命令列。引用如下: -Command 相對 -File設定檔和包裝腳本可能會影響最終狀態的歸屬進程。測試期間應分別捕獲輸出和退出程式碼,以避免正確的訊息掩蓋錯誤的狀態。

保留外部程式的故障

有時,包裝腳本需要從原生工具傳回確切的狀態。 $LASTEXITCODE 立即執行,因為運行另一個本地命令可以替換它。

git fetch --all
$gitExitCode = $LASTEXITCODE

if ($gitExitCode -ne 0) {
    Write-Error "git fetch failed with exit code $gitExitCode"
    exit $gitExitCode
}

如果原生工具傳回的程式碼超出可移植範圍,請將其對應到文件中記錄的腳本級程式碼。將原始程式碼包含在錯誤記錄或診斷輸出中。這樣既能為呼叫者提供穩定的接口,又不會遺失故障排除所需的資訊。

最終建議

將業務邏輯放在傳回資料或拋出錯誤的函數中,並將進程退出策略放在一個小型頂層腳本中。這種結構更易於測試、重複使用和託管。僅在控制結構可見的情況下使用循環控制關鍵字,並將強制進程終止保留在無法優雅關閉的情況下。