一行批处理用户提升(带参数)
以下是我对这个古老的批量用户提升问题的单行版本,这个问题在今天仍然是相关的。
只需将代码添加到批处理脚本的顶部,就可以开始了。
沉默
这个版本不会输出任何东西,也不会在出错时暂停执行。
@setlocal disabledelayedexpansion enableextensions
@echo off
:: Admin check
fltmc >nul 2>nul || set _=^"set _ELEV=1^& cd /d """%cd%"""^& "%~f0" %* ^"&&((if "%_ELEV%"=="" ((powershell -nop -c start cmd -args '/d/x/s/v:off/r',$env:_ -verb runas >nul 2>nul) || (mshta vbscript:execute^("createobject(""shell.application"").shellexecute(""cmd"",""/d/x/s/v:off/r ""&createobject(""WScript.Shell"").Environment(""PROCESS"")(""_""),,""runas"",1)(window.close)"^) >nul 2>nul)))& exit /b)
详细的
详细版本,告诉用户正在请求管理权限,并在退出前暂停错误。
@setlocal disabledelayedexpansion enableextensions
@echo off
:: Admin check
fltmc >nul 2>nul || set _=^"set _ELEV=1^& cd /d """%cd%"""^& "%~f0" %* ^"&&((if "%_ELEV%"=="" (echo Requesting administrator privileges...&((powershell -nop -c start cmd -args '/d/x/s/v:off/r',$env:_ -verb runas >nul 2>nul) || (mshta vbscript:execute^("createobject(""shell.application"").shellexecute(""cmd"",""/d/x/s/v:off/r ""&createobject(""WScript.Shell"").Environment(""PROCESS"")(""_""),,""runas"",1)(window.close)"^) >nul 2>nul))) else (echo This script requires administrator privileges.& pause))& exit /b)
echo Has admin permissions
echo Working dir: "%cd%"
echo Script dir: "%~dp0"
echo Script path: "%~f0"
echo Args: %*
pause
操作方法
Uses fltmc to check for administrator privileges. (system component, included in Windows 2000+)
If user already has administrator privileges, continues operation normally.
If not, spawns an elevated version of itself using either:
powershell (optional Windows feature, included in Windows 7+ by default, can be uninstalled/otherwise not available, can be installed on Windows XP/Vista)
mshta (system component, included in Windows 2000+)
If fails to acquire elevation, stops execution (instead of looping endlessly).
这个解决方案与其他解决方案的区别是什么?
实际上,解决这个问题的方法有数百种,但我迄今为止发现的所有方法都有其缺点,这是解决其中大多数问题的尝试。
Compatibility. Using fltmc as the means of checking for privileges and either powershell or mshta for elevation works with every Windows version since 2000 and should cover most system configurations.
Does not write any extra files.
Preserves current working directory. Most of the solutions found conflate "script directory" with "working directory" which are totally different concepts. If you want to use "script directory" instead, replace %cd% with %~dp0. Some people advocate using pushd "%~dp0" instead so paths inside networked UNC paths like "\\SOMEONES-PC\share" will work but that will also automagically map that location to a drive letter (like Y:) which might or might not be what you want.
Stops if unable to acquire elevation. This can happen because of several reasons, like user clicking "No" on the UAC prompt, UAC being disabled, group policy settings, etc. Many other solutions enter an endless loop on this point, spawning millions of command prompts until the heat death of the universe.
Supports (most of) command-line arguments and weird paths. Stuff like ampersands &, percent signs %, carets ^ and mismatching amount of quotes """'. You still definitely CAN break this by passing a sufficiently weird combinations of those, but that is an inherent flaw of Windows' batch processing and cannot really be worked around to always work with any combination. Most typical use-cases should be covered though and arguments work as they would without the elevation script.
已知的问题
If you enter a command-line argument that has a mismatched amount of double-quotes (i.e. not divisible by 2), an extra space and a caret ^ will be added as a last argument. For example "arg1" arg2" """" "arg3" will become "arg1" arg2" """" "arg3" ^. If that matters for your script, you can add logic to fix it, f.ex. check if _ELEV=1 (meaning that elevation was required) and then check if the last character of argument list is ^ and/or amount of quotes is mismatched and remove the misbehaving caret.
日志输出到文件的示例脚本
您不能轻易地使用>进行标准输出日志记录,因为在提升时将生成一个新的cmd窗口并切换执行上下文。
你可以通过传递越来越奇怪的转义字符组合来实现它,比如raise .bat testarg ^^^> test.txt,但你需要让它总是生成新的cmd窗口,或者添加逻辑来去掉插入符号,所有这些都增加了复杂性,在许多情况下它仍然会中断。
最好和最简单的方法是在批处理脚本中添加日志记录,而不是尝试从命令行重定向。这样你就不用再头疼了。
这里有一个例子,你可以很容易地实现你的脚本日志:
@setlocal disabledelayedexpansion enableextensions
@echo off
:: Admin check
fltmc >nul 2>nul || set _=^"set _ELEV=1^& cd /d """%cd%"""^& "%~f0" %* ^"&&((if "%_ELEV%"=="" (echo Requesting administrator privileges...&((powershell -nop -c start cmd -args '/d/x/s/v:off/r',$env:_ -verb runas >nul 2>nul) || (mshta vbscript:execute^("createobject(""shell.application"").shellexecute(""cmd"",""/d/x/s/v:off/r ""&createobject(""WScript.Shell"").Environment(""PROCESS"")(""_""),,""runas"",1)(window.close)"^) >nul 2>nul))) else (echo This script requires administrator privileges.& pause))& exit /b)
set _log=
set _args=%*
if not defined _args goto :noargs
set _args=%_args:"=%
set _args=%_args:(=%
set _args=%_args:)=%
for %%A in (%_args%) do (if /i "%%A"=="-log" (set "_log=>> %~n0.log"))
:noargs
if defined _log (echo Logging to file %~n0.log) else (echo Logging to stdout)
echo Has admin permissions %_log%
echo Working dir: "%cd%" %_log%
echo Script dir: "%~dp0" %_log%
echo Script path: "%~f0" %_log%
echo Args: %* %_log%
echo Hello World! %_log%
pause
执行:logtest.bat -log
通过添加参数-log,输出将记录到文件中,而不是记录到stdout。
封闭的思想
让我困惑的是,即使在UAC存在了15年之后,一个简单的“ELEVATE”指令还没有被引入到批处理中。也许有一天微软会振作起来。在那之前,我们只能求助于这些黑客。