将引用的参数从批处理文件传递到"powershell start"-按需自我提升 [英] passing quoted arguments from batch file to `powershell start` - self-elevation on demand
问题描述
如果用户在出现的用户访问控制"对话框中单击是",我正在编写一个Windows批处理文件,该文件将自动升级为管理权限.
I am writing a Windows batch file that automatically escalates itself to administrative permissions, provided the user clicks "Yes" on the User Access Control dialog that appears.
我正在使用在此处中学到的技术来检测我们是否已经拥有管理员权限,以及是否已经从
I am using a technique I learned here to detect whether we already have admin rights and another from here to escalate. When appropriate, the following script, let's call it foo.bat
, re-launches itself via a powershell-mediated call to 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"
中(其中第二个参数应该是两个单词,两个三"),则上述代码可以正常工作.
My problem is with escaping quotes in the -ArgumentList
. The code above works fine if I call foo.bat one two
from the command prompt, but not if one of the arguments contains a space, for example as in foo.bat one "two three"
(where the second argument should be two words, "two three").
如果用静态参数替换%*
时,我什至可以得到适当的行为:
If I could even just get the appropriate behavior when I replace %*
with static arguments:
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'
).在
then I could add some lines in foo.bat
that compose an appropriately-escaped substitute for %*
. However, even on that static example, every escape pattern I have tried so far has either failed (I see Second argument is "two"
rather than Second argument is "two three"
) or caused an error (typically Start-Process: A positional parameter cannot be found that accepts argument 'two'
). Drawing on the docs for powershell's Start-Process I have tried all manner of ridiculous combinations of quotes, carets, doubled and tripled quotes, backticks, and commas, but there's some unholy interaction going on between batch-file quoting and powershell quoting, and nothing has worked.
这有可能吗?
推荐答案
-
您遇到了一个两个引用地狱的完美风暴(
cmd
和 PowerShell),并带有You've run into a perfect storm of two quoting hells (
cmd
and PowerShell), garnished with a PowerShell bug (as of PowerShell Core 6.2.0).要解决此错误,不能直接 重新调用批处理文件,而必须通过
cmd /c
重新调用.To work around the bug, the batch file cannot be reinvoked directly and must instead be reinvoked via
cmd /c
.LotPings的有用答案(考虑到这一点),通常有效,但在以下情况下 not :
LotPings' helpful answer, which takes that into account, typically works, but not in the following edge cases:
- 如果批处理文件的完整路径包含空格(例如
c:\path\to\my batch file.cmd
) - 如果参数碰巧包含以下任何
cmd
元字符(甚至在"..."
内部):& | < > ^
;例如one "two & three"
- 如果使用管理员特权重新调用批处理文件依赖于在最初从其调用的同一工作目录中执行.
- If the batch file's full path contains spaces (e.g.,
c:\path\to\my batch file.cmd
) - If the arguments happen to contain any of the following
cmd
metacharacters (even inside"..."
):& | < > ^
; e.g.,one "two & three"
- If the reinvoked-with-admin-privileges batch file relies on executing in the same working directory it was originally called from.
以下解决方案解决了所有这些极端情况.尽管它并非无关紧要,但它应该可以按原样重复使用:
The following solution addresses all these edge cases. While it is far from trivial, it should be reusable as-is:
@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 start"-按需自我提升的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- 如果批处理文件的完整路径包含空格(例如