如何在不关闭电源外壳控制台的情况下从Powershell模块函数返回非零退出代码?



我的powershell模块有一个函数,我希望它返回一个非零退出代码。但是,作为一个模块函数,当我运行 Powershell 控制台时,它会加载到 powershell 控制台的上下文中Import-Module.因此,当函数执行exit 1 - 噗,进入控制台窗口!

至少,这就是我解释它在退出时关闭电源外壳窗口的方式。

那么,ps 模块函数如何在不终止导入模块的控制台的情况下以非零退出代码退出呢?

附言

我确实注意到关于这个主题的几个问题,但似乎没有人研究这个特殊情况。

编辑 1

我想提供一些背景信息。我有一个有很多功能的PS模块。其中一些在 Azure DevOps yaml 生成脚本中按原样使用。后者知道识别非零退出代码并中止管道,因此无需从函数中抛出来中止流。

但是,如果我想从控制台调用该函数,例如快速测试某些内容并且它以非零代码退出,则整个控制台窗口将关闭。这非常烦人。

有时有一种解决方法。因此,代替此代码:

dotnet build ...
$ExitCode = $LastExitCode
DoSomethingInAnyCase()
if ($ExitCode)
{
    exit $ExitCode
}

我们可以有以下版本:

try
{
    dotnet build ...
}
finally
{
    DoSomethingInAnyCase()
}

两个版本都将正确返回正确的退出代码,但由于第二个版本没有显式 exit 语句,因此它不会关闭控制台。

您必须设置$global:LASTEXITCODE才能设置退出代码,但请注意,PowerShell 函数实际上并不是为了设置退出代码,而只是脚本,后者仅用于通过 PowerShell 进程自己的退出代码向外界报告退出代码,当通过其 (Windows PowerShell 的 CLI powershell.exe调用时, 例如,pwsh适用于 PowerShell 核心版(,来自构建工具、计划任务或其他外壳。

另请注意,设置直接$global:LASTEXITCODE

  • 不会使自动成功状态变量 $? 反映调用方上下文中的$falseexit <nonzero-value>从脚本执行的方式以及调用报告非零退出代码的外部程序

    的方式。
  • 不足以使 PowerShell 进程作为一个整体报告此退出代码。

简而言之:所有这些好处是调用方可以在调用函数后检查$LASTEXITCODE,就像调用外部程序后一样。


通常,退出代码是一个进程间概念,不适合 PowerShell 的进程内环境。

有关 PowerShell 中的退出代码的详细信息,请参阅此帖子。


PowerShell的模拟退出代码是$?,自动的布尔成功状态变量($true$false(,它反映了PowerShell命令之后的成功。
(如果该命令是外部程序调用,则退出代码 0 $?设置为 $true ,任何非零命令将其设置为 $false (。

事实证明,设置才是你真正想问的。

从PowerShell Core 7.0.0-preview.5开始,无法直接设置$?

目前,这些是使$?在调用方的作用域中反映$false的唯一方法,相反,您无法始终确保它$true

  • 从脚本:使用exit <nonzero-integer>退出脚本

  • 从 cmdlet 或函数(脚本(中:

    • 抛出脚本终止错误(Throw(或语句终止错误($PSCmdlet.ThrowTerminatingError()( - 后者仅在高级函数和脚本中可用。

    • 使用 $PSCmdlet.WriteError()将错误写入 PowerShell 的错误流 - 后者仅在高级函数和脚本中可用。

      • 请注意,这意外地当前不适用于 Write-Error cmdlet - 请参阅此 GitHub 问题

请注意,这两种技术都总是涉及发出错误

由于听起来这正是您要避免的,因此您必须等到直接设置$?的功能实现。

已经决定实施此功能,但尚不清楚何时实施。

解决办法:运行cmd.exe并在退出函数之前设置所需的任何退出代码。例:

function Test-Function {
  $cmd = Join-Path ([Environment]::GetFolderPath([Environment+SpecialFolder]::System)) "cmd.exe"
  & $cmd /c exit 3
  # $LASTEXITCODE will be set to 3
}

最新更新