powershell - 将带引号的参数从批处理文件传递给 `powershell start` - 按需 self 提升

标签 powershell batch-file escaping quotes elevated-privileges

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

我正在使用我学到的技术 here检测我们是否已经拥有管理员权限以及来自 here 的另一个权限升级。在适当的时候,下面的脚本,我们称之为 foo.bat ,通过以 powershell 为中介的调用重新启动 runas :

@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

我的问题是在 -ArgumentList 中转义引号.如果我调用 foo.bat one two,上面的代码工作正常从命令提示符开始,但如果其中一个参数包含空格,则不会,例如在 foo.bat one "two three" 中(其中第二个参数应该是两个词,“二三”)。

如果我在替换 %* 时甚至可以得到适当的行为使用静态参数:
powershell start -wait -verb runas "%~dpfx0" -ArgumentList 'one "two three"'

然后我可以在 foo.bat 中添加一些行构成 %* 的适当转义替代品.然而,即使在那个静态示例中,到目前为止我尝试过的每个转义模式要么失败(我看到的是 Second argument is "two" 而不是 Second argument is "two three" )或导致错误(通常是 Start-Process: A positional parameter cannot be found that accepts argument 'two' )。上图 the docs for powershell's Start-Process我已经尝试了各种可笑的引号、插入符号、双引号和三引号、反引号和逗号的组合,但是批处理文件引用和 powershell 引用之间存在一些邪恶的交互,但没有任何效果。

这甚至可能吗?

最佳答案

  • 您遇到了两个引用 hell (cmd 和 PowerShell)的完美 Storm ,并以 PowerShell bug 装饰。 (从 PowerShell Core 6.2.0 开始)。
  • 要解决该错误,不能直接重新调用批处理文件,而必须通过 cmd /c 重新调用。 .
  • LotPings' helpful answer ,考虑到这一点,通常有效,但不适用于以下边缘情况:
  • 如果批处理文件的完整路径包含空格(例如, c:\path\to\my batch file.cmd )
  • 如果参数恰好包含以下任何一项 cmd元字符(甚至在 "..." 内):& | < > ^ ;例如,one "two & three"
  • 如果 reinvoked-with-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
    

    关于powershell - 将带引号的参数从批处理文件传递给 `powershell start` - 按需 self 提升,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54658352/

    相关文章:

    powershell - 将变量的原始内容粘贴到另一个命令的过滤器中

    batch-file - 使用 .bat 附加文本

    batch-file - 获取文件中的结果计数(按前 3 个字符)。

    c++ - 如何在 C++ 中输出两个版本的字符串,一个带有转义字符,另一个没有?

    azure - 无法通过端口 445 访问 Azure 存储帐户

    powershell - 定义 PowerShell 模块中所有函数通用的参数

    windows - 如果...或如果...在 Windows 批处理文件中

    java - 字符串中的转义字符

    java - 如何转义 IntelliJ 文件模板中的 Maven 属性

    powershell - Azure Powershell 模板 : return Boolean if all resource provisioned successfully?