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 可能会沿调用堆栈查找外层控制结构;如果找不到,当前运行空间可能被终止。
使用 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 启动的子进程会把控制权交还调用方;交互式控制台可能关闭当前会话。嵌入式运行空间和编辑器的行为可能不同。应分别测试实际生产命令行中的 -File 与 -Command、配置文件、引号、输出和退出状态。
建议
只在有意的顶层边界使用 exit <代码>,用 return 离开当前作用域,用 throw 报告调用方可处理的失败。只在可见的循环或 switch 中使用 break 和 continue,并且仅用 Stop-Process 终止独立的本地进程。返回数据或抛出错误的函数可保持复用;小型入口脚本可将结果转换为 shell、计划程序、CI 或包装程序可用的稳定状态。