如何防止主机退出和 return 退出代码?

How to prevent exit of host and return exit code?

Ansgar Wiechers 的回答在启动新的 PowerShell 进程时效果很好。 这适用于 cmd.exe 和 powershell.exe。

C:>type .\exit1.ps1
function ExitWithCode($exitcode) {
  $host.SetShouldExit($exitcode)
  exit $exitcode
}
ExitWithCode 23

在 cmd.exe 互动中 shell。

C:>powershell -NoProfile -Command .\exit1.ps1
C:>echo %ERRORLEVEL%
23
C:>powershell -NoProfile -File .\exit1.ps1
C:>echo %ERRORLEVEL%
23

在 PowerShell 交互中 shell。

PS C:>powershell -NoProfile -Command .\exit1.ps1
PS C:>$LASTEXITCODE
23
PS C:>powershell -NoProfile -File .\exit1.ps1
PS C:>$LASTEXITCODE
23

然而...运行现有交互式 PowerShell 主机中的 .ps1 脚本将完全退出主机。

PS C:>.\exit1.ps1
    <<<poof! gone! outahere!>>>

如何防止它退出主机 shell?

How can I prevent it from exiting the host shell?

您可以检查当前 运行 PowerShell 进程是否是另一个 PowerShell 父进程的子进程,并且只有在该条件为真时才调用 $host.SetShouldExit()。例如:

function ExitWithCode($exitcode) {
   # Only exit this host process if it's a child of another PowerShell parent process...
   $parentPID = (Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$PID" | Select-Object -Property ParentProcessId).ParentProcessId
   $parentProcName = (Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$parentPID" | Select-Object -Property Name).Name
   if ('powershell.exe' -eq $parentProcName) { $host.SetShouldExit($exitcode) }

   exit $exitcode
}
ExitWithCode 23

希望对您有所帮助。

不要使用$host.SetShouldExit():它不是由用户代码调用的。 相反,PowerShell 在内部 使用它来响应用户代码 .

中的 exit 语句

只需在您的 exit1.ps1 脚本中直接使用 exit 23,它将执行您想要的操作:

  • 当 运行 在 PowerShell 会话中 时,脚本将设置退出代码 23 而不退出整个 PowerShell 进程;之后使用$LASTEXITCODE查询。

      .\exit.ps1; $LASTEXITCODE # -> 23
    
  • 当运行 通过PowerShell CLI:

  • -File,脚本设置的退出代码自动成为PowerShell进程的退出代码,调用者可以查看;当从 cmd.exe 调用时,%ERRORLEVEL% 反映退出代码。

         powershell -File .\exit.ps1
         :: This outputs 23
         echo %ERRORLEVEL%
    
  • -Command需要额外的工作,因为 PowerShell 然后只需 将任何非零退出代码映射到 1,这会导致特定的退出代码丢失;为了弥补这一点,只需 执行 exit $LASTEXITCODE 作为最后一条语句 :

         powershell -Command '.\exit.ps1; exit $LASTEXITCODE'
         :: This outputs 23
         echo %ERRORLEVEL%
    

有关 PowerShell 如何设置退出代码的更多信息


如果:

  • 您无法控制通过 CLI 调用脚本的方式,但必须确保报告正确的退出代码,即使通过 -Command 调用脚本也是如此,

  • 您愿意承担使用$host.SetShouldExit()的风险,即使它不是为直接使用而设计的,

您可以尝试以下方法:

function ExitWithCode($exitcode) {
  if ([Environment]::CommandLine -match ( # Called via the CLI? (-File or -Command)
    ' .*?\b' + 
    [regex]::Escape([IO.Path]::GetFileNameWithoutExtension($PSCommandPath)) +
    '(?:\.ps1\b| |$)')
  ) {
    # CAVEAT: While this sets the exit code as desired even with -Command,
    #         the process terminates instantly.
    $host.SetShouldExit($exitcode)
  }
  else {
    # Exit normally, which in interactive session exits the script only.
    exit $exitcode
  }
}

ExitWithCode 23

函数在进程命令行上查找执行脚本的文件名,以检测是否通过 CLI、自动 $PSCommandPath 变量,其中包含脚本的完整路径。

如果是这样,应用 $host.SetShouldExit() 调用以确保即使在通过 -Command.
调用的情况下也按预期设置退出代码 请注意,这相当于有效 内部 .SetShouldExit() 方法的 重新利用
令人惊讶的是,即使在 -Command 字符串中的脚本调用之后出现其他命令,这种重新利用也能奏效,但请注意,这总是意味着真正最后一个命令的成功状态——如果它不是脚本调用的话——是有效忽略。

这种方法并非万无一失[1],但在实践中可能效果很好。


[1]

  • 可能存在 误报,假设只查找文件 name,没有扩展名(因为 -Command允许省略被调用脚本的 .ps1 扩展名)。
  • 如果脚本是通过另一个脚本或通过别名调用的,则可能存在 漏报