将带引号的参数从批处理文件传递到“PowerShell Start”- 按需自行提升

passing quoted arguments from batch file to `powershell start` - self-elevation on demand

提问人:jez 提问时间:2/13/2019 最后编辑:mklement0jez 更新时间:4/24/2023 访问量:1688

问:

我正在编写一个 Windows 批处理文件,该文件会自动升级为管理权限,前提是用户在出现的“用户访问控制”对话框中单击“是”。

我正在使用我在这里学到的一种技术来检测我们是否已经拥有管理员权限,并从这里升级另一种技术。在适当的时候,以下脚本(我们称之为 )通过 powershell 中介的调用重新启动自身:foo.batrunas

@echo off
net session >NUL 2>NUL
if %ERRORLEVEL% NEQ 0 (
powershell start -wait -verb runas "%~dpfx0" -ArgumentList '%*'
goto :eof
)

echo Now we are running with admin rights
echo First argument is "%~1"
echo Second argument is "%~2"
pause

我的问题是转义 .如果我从命令提示符调用,上面的代码可以正常工作,但如果其中一个参数包含空格,则不能正常工作,例如 in (其中第二个参数应该是两个单词,“两个三”)。-ArgumentListfoo.bat one twofoo.bat one "two three"

如果我甚至可以在用静态参数替换时获得适当的行为:%*

powershell start -wait -verb runas "%~dpfx0" -ArgumentList 'one "two three"'

然后我可以添加一些行,组成一个适当转义的替代 .但是,即使在该静态示例中,到目前为止,我尝试过的每个转义模式都失败了(我看到而不是),或者导致了错误(通常)。借鉴 powershell 的 Start-Process 文档,我尝试了引号、插入符号、双引号和三引号、反引号和逗号的各种荒谬组合,但批处理文件引用和 powershell 引用之间发生了一些不神圣的交互,但没有任何效果。foo.bat%*Second argument is "two"Second argument is "two three"Start-Process: A positional parameter cannot be found that accepts argument 'two'

这甚至可能吗?

PowerShell 批处理文件 转义 引用提升 的权限

评论

0赞 AdminOfThings 2/13/2019
您的代码在命令提示符下的 Windows 10 上对我有用。您运行的是什么操作系统或命令 shell 版本?
0赞 jez 2/13/2019
@AdminOfThings 但我猜你看到的而不是想要的Second argument is "two"Second argument is "two three"
0赞 AdminOfThings 2/13/2019
在 UAC 打开的 Windows 7 上运行时,代码会打开一个命令提示符窗口,第二个参数为“two”。在我原来的命令提示符控制台中,关闭弹出控制台后显示“二三”。在 Windows 10 上,我只收到第二个参数是“二三”,没有额外的命令窗口。它可能不会在我的 Windows 10 系统上执行。
1赞 jez 2/13/2019
@AdminOfThings,如果您看到原始控制台中打印了某些内容,则一定是丢失了,或者以某种方式被忽略了。哎呀,我在问题中的列表中缺少它,这就是编辑的原因。重要的是批处理文件的提升实例的参数是什么,它们应该与未提升版本中的参数相同。goto :eof
2赞 aschipfl 2/13/2019
什么是 ?我猜你想要完整的路径,所以在长形式或更紧凑的形式中......%~dpfx0%~dpnx0%~f0

答:

2赞 user6811411 2/13/2019 #1

这是我用于此目的的批次:

::ElevateMe.cmd::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@echo off & setlocal EnableExtensions DisableDelayedExpansion
Set "Args=%*"
net file 1>nul 2>&1 || (powershell -ex unrestricted -Command ^
  Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c %~f0 %Args:"=\""%'
  goto :eof)
:: Put code here that needs elevation
Echo:%*
Echo:%1
Echo:%2
Pause

示例输出:

one "two three"
one
"two three"
Drücken Sie eine beliebige Taste . . .

如果希望提升的 cmd 保持打开状态,请使用-ArgumentList '/k %~f0 %Args:"=\""%

评论

1赞 jez 2/14/2019
太棒了,谢谢。值得注意的是,您的解决方案也适用于空的情况(我的原来没有,因为您无法传递空%*-ArgumentList)
-1赞 Noodles 2/14/2019 #2

唯一批准的提升方法是使用清单。这模拟了Unix的SUDO.EXE

运行命令并保持提升状态

RunAsAdminconsole <Command to run>

提升当前 cmd 窗口或创建新的提升窗口

RunAsAdminconsole 

https://pastebin.com/KYUgEKQv


REM Three files follow
REM RunAsAdminConsole.bat
REM This file compiles RunAsAdminconsole.vb to RunAsAdminconsole.exe using the system VB.NET compiler.
REM Runs a command elevated using a manifest
C:\Windows\Microsoft.NET\Framework\v4.0.30319\vbc "%~dp0\RunAsAdminconsole.vb" /win32manifest:"%~dp0\RunAsAdmin.manifest" /out:"%~dp0\RunAsAdminConsole.exe" /target:exe
REM To use
rem RunAsAdminconsole <Command to run>
pause

RunAsAdmin.manifest

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="*"
    name="Color Management"
    type="win32"
/>
<description>Serenity's Editor</description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> 
<security> 
    <requestedPrivileges> 
        <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/> 
    </requestedPrivileges> 
</security> 
</trustInfo> 
</assembly>

'RunAsAdminConsole.vb
'Change cmd /k to cmd /c to elevate and run command then exit elevation
imports System.Runtime.InteropServices 
Public Module MyApplication  

    Public Sub Main ()
        Dim wshshell as object
        WshShell = CreateObject("WScript.Shell")
        Shell("cmd /k " & Command())
    End Sub 

End Module 


----------------------------------------

评论

1赞 Compo 2/14/2019
它应该已经是带有尾部反斜杠的扩展了!%~dp0RunAsAdmin...%~dp0
0赞 Noodles 2/14/2019
这没什么区别。VBC 不介意双反斜杠。有一天,我将运行一个带有三个反斜杠的实验,以测试我的假设,即它将其视为转义的反斜杠。它使批处理文件路径更加明显。
1赞 Compo 2/14/2019
我没有说它有什么不同,但仅仅因为它在这种情况下没有,并不足以让它显示为不正确。我没有看到您的其余代码,只是包含不必要的额外反斜杠!
0赞 Noodles 2/14/2019
它是许多程序的模板。我使用长程序名称。这样可以很容易地编辑为较短的名称。
0赞 mklement0 2/14/2019
顺便说一句,@Compo的观点是有道理的,即使它没有实际区别。重新声明您唯一批准的方式应该适用于按需提升并且通常有效,但是在传递双引号参数方面存在错误。要求使用两个辅助文件进行按需编译 - 使用硬编码路径,同样如此 - 不是一个实用的替代方法。Start-Process -Verb RunAs
12赞 mklement0 2/15/2019 #3
  • 你遇到了两个引用地狱( PowerShell)的完美风暴,并带有 PowerShell 错误(从 PowerShell Core 6.2.0 开始)。cmd

  • 要解决此错误,不能直接重新调用批处理文件,而必须通过 重新调用。cmd /c

  • 考虑到这一点,LotPings 的有用答案通常有效,但在以下边缘情况下无效

    • 如果批处理文件的完整路径包含空格(例如,c:\path\to\my batch file.cmd)
    • 如果参数恰好包含以下任何元字符(甚至在内部): ;例如,cmd"..."& | < > ^one "two & three"
    • 如果重新调用的 admin-privileges 批处理文件依赖于在最初调用它的同一工作目录中执行。

以下解决方案解决了所有这些边缘情况。虽然它远非微不足道,但它应该是可按原样重用的:

@echo off & setlocal

:: Test whether this invocation is elevated (`net session` only works with elevation).
:: If already running elevated (as admin), continue below.
net session >NUL 2>NUL && goto :elevated

:: If not, reinvoke with elevation.
set args=%*
if defined args set args=%args:^=^^%
if defined args set args=%args:<=^<%
if defined args set args=%args:>=^>%
if defined args set args=%args:&=^&%
if defined args set args=%args:|=^|%
if defined args set "args=%args:"=\"\"%"
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
  " Start-Process -Wait -Verb RunAs -FilePath cmd -ArgumentList \"/c \"\" cd /d \"\"%CD% \"\" ^&^& \"\"%~f0\"\" %args% \"\" \" "
exit /b

:elevated

:: =====================================================
:: Now we are running elevated, in the same working dir., with args passed through.
:: YOUR CODE GOES HERE.

echo First argument is "%~1"
echo Second argument is "%~2"

pause

注意:

  • 上面后面的空格,之前的空格不是偶然的,实际上在从驱动器的根目录调用时,也需要使批处理文件工作 - 请参阅此答案,它还提供了上述解决方案的更模块化的替代方案,借助 调用的子例程%CD%\"call

评论

1赞 N Jones 11/28/2019
我希望我能投 10 倍的票!这是我第一次看到真正适用于特殊字符和空格的片段。