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

3

我的PowerShell模块有一个函数,我希望它返回非零的退出代码。但是,由于模块函数在运行Import-Module时被加载到PowerShell控制台的上下文中,所以当该函数执行exit 1命令时,控制台窗口就会关闭。

至少,这就是我解释当函数退出时为什么会关闭PowerShell窗口的方式。

那么,如何使PS模块函数在不关闭导入模块的控制台的情况下退出并返回非零的退出代码呢?

P.S.

我注意到Stack Overflow上有几个关于此主题的问题,但没有一个似乎涉及到这种特殊情况。

编辑 1

我想提供一些背景信息。我有一个PS模块,其中包含许多函数。其中一些函数在Azure DevOps yaml构建脚本中直接使用。后者知道如何识别非零退出代码并终止流程,因此没有必要从函数中抛出来终止流程。

然而,如果我想从控制台调用该函数,例如快速测试某些内容并且它返回非零的退出代码,则整个控制台窗口都会关闭。这非常令人烦恼。

有时会有一些解决方法。因此,可以使用以下代码而不关闭控制台窗口:

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

我们可以有以下版本:
try
{
    dotnet build ...
}
finally
{
    DoSomethingInAnyCase()
}

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


你想通过需要一个非零的退出代码来解决什么问题? - Bill_Stewart
我想在没有显式抛出语句的情况下指示失败。 - mark
为什么?抛出异常有什么问题吗? - Bill_Stewart
屏幕上有太多垃圾,这是它的结果。我经常使用它,但我想了解退出代码,因此提出了这个问题。 - mark
我正在努力理解你的目标。模块实现在当前作用域中执行的函数。函数不会更改退出代码,因为这些代码仅由进程或脚本设置。虽然我猜你可以编写 & $Env:ComSpec /c exit n - Bill_Stewart
显示剩余6条评论
2个回答

6
您需要设置$global:LASTEXITCODE来设置退出代码,但请注意,PowerShell 函数并不是真正意义上的退出代码的设置者,只是针对脚本,而后者仅通过 PowerShell 进程自己的退出代码向外界报告退出代码,当 PowerShell 通过它的 (CLI powershell.exe 用于 Windows PowerShell,pwsh 用于 PowerShell Core) 从构建工具、计划任务或另一个 shell 等调用时。

还要注意直接设置 $global:LASTEXITCODE:

  • 不能使调用者上下文中的自动成功状态变量 $? 反映出 $false,就像脚本中的 exit <nonzero-value> 和调用报告非零退出代码的外部程序一样。

  • 这还不足以使整个 PowerShell 进程报告此退出代码。

总之:这一切只是为了让调用者在调用您的函数后可以检查 $LASTEXITCODE,就像在调用外部程序后一样


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

有关 PowerShell 中退出代码的更多信息,请参见此帖子


PowerShell 对退出代码的类比是$?,自动的布尔成功状态变量 ($true$false),它反映了 PowerShell 命令的执行结果。
(如果该命令是对外部程序的调用,则 0 的退出代码将把 $? 设置为 $true,而任何非零值则将其设置为$false)。

事实证明,您真正想要问的是设置那个

截至 PowerShell Core 7.0.0-preview.5,您无法直接设置$?

目前,这些是导致调用者作用域中的 $? 反映为 $false 的唯一方法,反之,您不能始终确保它是 $true

  • 通过脚本:使用exit <非零整数>退出脚本

  • 通过cmdlet或function(脚本也适用):

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

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

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

请注意,两种技术都涉及发出一个错误

由于这似乎正是您试图避免的,您必须等待直接设置$?的能力被实现

已经决定实现这种能力,但不清楚何时实现。


1

解决方法:运行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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接