通过 PowerShell 以提升的权限调用批处理文件并检索退出代码

Call batch file with elevated privileges via PowerShell and retrieve exit code

我的 Windows 批处理应由 没有 管理员权限的用户启动。在某个步骤中,它将以提升的权限 称呼自己。我了解到这可以使用 PowerShell 的 runas 功能 (batch.bat ⭢ PowerShell ⭢ batch.bat)。这很有魅力。

不幸的是,我无法从提升的批处理执行中收到退出代码。我总是得到 1,尽管没有任何错误消息。我不知道退出代码在哪个 return 丢失,第一次(批量返回到 PowerShell)或第二次(PowerShell 返回到批量)。

我相信,我已经尝试了类似问题的所有建议答案,但显然我无法继续下去。我需要建议。

MVE 应该表示提升批次 returns 0:

@echo off
echo param=%~1
openfiles /local >nul 2>&1

IF %ERRORLEVEL% EQU 0 (
    echo elevated, exit 0
    pause
    exit 0
) ELSE (
    echo not elevated. trying to elevate.
    powershell start-process -wait -verb runas '%0' -argumentlist /foo
    echo powershell returned %errorlevel%.
)

注意事项(已编辑以消除误解):虽然非提升调用(由用户)不需要任何参数,但提升调用引入了一个额外的参数'/咕'。这让事情变得更糟,因为我没有找到不丢失此参数的解决方案。然而,这似乎是一个相当不寻常的用例。

您没有将要执行的 PowerShell 命令放在引号中,最好使用完整路径并将任何参数包含到脚本中。调用它的通用方法,因此它可以跨脚本复制,您的 PowerShell 调用应如下所示:

powershell -c "if([bool]'%*'){ Start-Process -Wait -Verb runas '%~dpnx0' -ArgumentList ('%*' -split '\s+') } else { Start-Process -Wait -Verb runas '%~dpnx0' }"

对于您的上述需求,这可以简化,因为您知道您已将参数传递到批处理文件中以进行处理:

powershell -c "Start-Process -Wait -Verb runas '%~dpnx0' -ArgumentList '/foo'
  • %~dpnx0 - 自动批处理变量,这是当前脚本的完整路径,包括脚本名称
  • %* - 自动批处理变量,这是传递给脚本的所有参数。
  • ('%*' -split '\s'):这是一个 PowerShell 表达式,采用 space 分隔的 %* 变量并将其拆分为连续的白色 space,返回一个数组。为简单起见,这确实有一个缺点,它会在双引号之间的 spaces 上拆分,但如果需要,可以调整正则表达式以解决这个问题。

This answer 值得一读,了解您将来可能会用到的其他自动批处理变量。

要解决参数问题,你可以使用

powershell start-process -wait -verb runas '%0' -argumentlist '/additional-arg %*'

退出代码问题:
第一个问题是行

echo powershell returned %errorlevel%.

这是行不通的,因为它在一个代码块内,并且 %errorlevel% 甚至在调用 powershell 之前就会展开,因此它始终为 1 - openfiles /local ... [=16 的结果=]

但即使延迟扩展,我也总是得到 0,可能是因为它是成功 runas 的退出代码,它能够启动您的批次。

您可以使用变通方法并将 退出代码 存储在临时文件中

@echo off
setlocal EnableDelayedExpansion
echo param=%*
openfiles /local >nul 2>&1

IF %ERRORLEVEL% EQU 0 (
    echo elevated, exit 13
    pause
    echo 13 > "%temp%\_exitcode.tmp"
    rem *** using 14 here to show that it doesn't be stored in errorlevel
    exit 14
) ELSE (
    echo not elevated. trying to elevate.
    powershell start-process -wait -verb runas '%0' -argumentlist '/additional-arg %*'
    set /p _exitcode= < "%temp%\_exitcode.tmp"
    del "%temp%\_exitcode.tmp"
    echo powershell returned !_exitcode!, lvl !errorlevel!.
)