在 PowerShell 中想提前結束程式有好幾種寫法,例如 return、exit,之前沒認真比過差異,胡亂用踩了坑,特整理筆記備忘。

先用以下 Test-ExitScript.ps1 程式示範,依傳入參數模擬四種提前結束 Script 的做法 - return、break、exit 及 [Environment]::Exit(),程式最後有個 Write-Host 'You should not see this',正常狀況不會顯示,用以驗證程式已提前中止。

param([string]$method = 'return')
Write-Host "Terminating script with '$method'"
if ($method -eq 'nothing') {
    # do nothing
}
elseif ($method -eq 'return') {
    return
}
elseif ($method -eq 'break') {
    break
}
elseif ($method -eq 'exit') {
    exit
}
elseif ($method -eq 'dos-exit') {
    [Environment]::Exit(255)
}
else {
    throw "Unkown method - $method"
}
Write-Host 'You should not see this'

第一組測試用 powershell .\\Test-ExitScript.ps1 xxxx方式執行,四種方法都正確提前結束程式。

但如果使用 PowerShell 主控台 或先執行 PowerShell.exe 進入互動模式,結果則有點不同。如下圖,我在 Cmder 先輸入 powershell 進入互動模式,接著呼叫 .\Test-ExitScript.ps1 xxxx 進行測試,return、break、exit 都正常,但 [Environment]::Exit() 會結束 PowerShell 互動模式回到 Cmder:

有趣的是,如果把同樣程式包成 Function 裡,結果又會不相同。

function TestExit($method) {
    Write-Host "Terminating script with '$method'"
    if ($method -eq 'nothing') {
        # do nothing
    }
    elseif ($method -eq 'return') {
        return
    }
    elseif ($method -eq 'break') {
        break
    }
    elseif ($method -eq 'exit') {
        exit
    }
    elseif ($method -eq 'dos-exit') {
        [Environment]::Exit(255)
    }
    else {
        throw "Unkown method - $method"
    }
    Write-Host 'You should not see this'
}

實測如下,先執行 powershell (1) 進入互動模式,用 . .\Test-ExitScriptInFunc.ps1 載入 function TestExit($method) 函式 (2),接著呼叫 TestExit xxxx 測試不同退出方法 (3),發現 exit (4) 跟 [Environment]::Exit() 都會導致 PowerShell 互動環境結束:

【結論】

由結果來看,若以排程執行單一 .ps1,四個方法都可提前結束執行,但 break 原本用於跳出 loop、while,移作他用易造成別人困惑,不推。exit 關鍵字 及 [Environment]::Exit() 搭配 Exit Code,可供 .BAT 判斷 %ERRORLEVEL% 做不同處理,有額外的應用場合。

但在 PowerShell 互動模式執行 .ps1 時,[Environment]::Exit() 有中止 PowerShell 互動環境的副作用。

如果要在 function 結束執行,建議乖乖用 return 就好,exit 跟 [Environment]::Exit() 都會強迫關閉 PowerShell 環境。我這次踩到的雷便是發生將 .ps1 包成 PowerShell 模組的過程,原本 .ps1 中用 exit 相安無事,改成 function 後安裝成模組,呼叫函式時發現 PowerShell 主控台會閃退,經過一番研究才搞懂是怎麼一回事。

大家如果有在 .ps1 使用 exit,改寫成函式或模組時,要留意這個小坑。

Difference among exit, break, return and [Environment]::Exit() in PowerShell.


Comments

# by Huang

function搭配return,結束搭配exit應該是不會搞錯

Post a comment