我希望我的批处理文件只运行提升。如果没有提升,为用户提供一个选项,以提升的方式重新启动批处理。

我正在编写一个批处理文件来设置一个系统变量,将两个文件复制到Program files位置,并启动一个驱动程序安装程序。如果Windows 7/Windows Vista用户(启用了UAC,即使他们是本地管理员)在没有右键单击并选择“以管理员身份运行”的情况下运行它,他们将得到“访问拒绝”,复制这两个文件并写入系统变量。

如果用户实际上是管理员,我想使用一个命令自动重新启动提升的批处理。否则,如果他们不是管理员,我想告诉他们,他们需要管理员权限来运行批处理文件。我使用xcopy复制文件和REG ADD写入系统变量。我正在使用这些命令来处理可能的Windows XP机器。我在这个主题上发现了类似的问题,但没有一个是关于重新启动批处理文件的。


您可以使用psexec的-h选项让脚本调用自身以提升运行。

我不确定你如何检测到它是否已经处于升高状态……也许只有在出现“访问被拒绝”错误时才会重新尝试提升烫发?

或者,你可以简单地让xcopy和reg.exe命令总是使用psexec -h运行,但是如果最终用户每次都需要输入密码(或者如果你在脚本中包含密码则不安全),这对他们来说会很烦人……


有一种简单的方法,不需要使用外部工具——它在Windows 7、8、8.1、10和11上运行良好,并且向后兼容(Windows XP没有任何UAC,因此不需要提升——在这种情况下,脚本就会继续)。

看看这段代码(我受到了NIronwolf在线程批处理文件中发布的代码的启发-在Windows 7上“访问被拒绝”?),但我已经改进了它-在我的版本中,没有任何目录创建和删除来检查管理员权限):

::::::::::::::::::::::::::::::::::::::::::::
:: Elevate.cmd - Version 4
:: Automatically check & get admin rights
:: see "https://stackoverflow.com/a/12264592/1016343" for description
::::::::::::::::::::::::::::::::::::::::::::
 @echo off
 CLS
 ECHO.
 ECHO =============================
 ECHO Running Admin shell
 ECHO =============================

:init
 setlocal DisableDelayedExpansion
 set cmdInvoke=1
 set winSysFolder=System32
 set "batchPath=%~dpnx0"
 rem this works also from cmd shell, other than %~0
 for %%k in (%0) do set batchName=%%~nk
 set "vbsGetPrivileges=%temp%\OEgetPriv_%batchName%.vbs"
 setlocal EnableDelayedExpansion

:checkPrivileges
  NET FILE 1>NUL 2>NUL
  if '%errorlevel%' == '0' ( goto gotPrivileges ) else ( goto getPrivileges )

:getPrivileges
  if '%1'=='ELEV' (echo ELEV & shift /1 & goto gotPrivileges)
  ECHO.
  ECHO **************************************
  ECHO Invoking UAC for Privilege Escalation
  ECHO **************************************

  ECHO Set UAC = CreateObject^("Shell.Application"^) > "%vbsGetPrivileges%"
  ECHO args = "ELEV " >> "%vbsGetPrivileges%"
  ECHO For Each strArg in WScript.Arguments >> "%vbsGetPrivileges%"
  ECHO args = args ^& strArg ^& " "  >> "%vbsGetPrivileges%"
  ECHO Next >> "%vbsGetPrivileges%"
  
  if '%cmdInvoke%'=='1' goto InvokeCmd 

  ECHO UAC.ShellExecute "!batchPath!", args, "", "runas", 1 >> "%vbsGetPrivileges%"
  goto ExecElevation

:InvokeCmd
  ECHO args = "/c """ + "!batchPath!" + """ " + args >> "%vbsGetPrivileges%"
  ECHO UAC.ShellExecute "%SystemRoot%\%winSysFolder%\cmd.exe", args, "", "runas", 1 >> "%vbsGetPrivileges%"

:ExecElevation
 "%SystemRoot%\%winSysFolder%\WScript.exe" "%vbsGetPrivileges%" %*
 exit /B

:gotPrivileges
 setlocal & cd /d %~dp0
 if '%1'=='ELEV' (del "%vbsGetPrivileges%" 1>nul 2>nul  &  shift /1)

 ::::::::::::::::::::::::::::
 ::START
 ::::::::::::::::::::::::::::
 REM Run shell as admin (example) - put here code as you like
 ECHO %batchName% Arguments: P1=%1 P2=%2 P3=%3 P4=%4 P5=%5 P6=%6 P7=%7 P8=%8 P9=%9
 cmd /k

该脚本利用了NET FILE需要管理员权限这一事实,如果没有管理员权限,则返回errorlevel 1。提升是通过创建一个脚本来实现的,该脚本重新启动批处理文件以获得特权。这将导致Windows显示UAC对话框,并要求您输入管理员帐户和密码。

我已经在Windows 7、8、8.1、10、11和Windows XP上测试过了,在所有操作系统上都运行良好。 这样做的好处是,在起始点之后,您可以放置任何需要系统管理员权限的东西,例如,如果您打算为了调试目的重新安装和重新运行Windows服务(假设mypackage。Msi是一个服务安装包):

msiexec /passive /x mypackage.msi
msiexec /passive /i mypackage.msi
net start myservice

如果没有这个特权提升脚本,UAC会三次询问您的管理员用户名和密码——现在您只会在开始时被询问一次,而且是在需要的时候。


如果你的脚本只需要显示一个错误消息,并在没有任何管理员权限时退出,而不是自动提升,这甚至更简单:你可以通过在脚本开头添加以下内容来实现:

@ECHO OFF & CLS & ECHO.
NET FILE 1>NUL 2>NUL & IF ERRORLEVEL 1 (ECHO You must right-click and select &
  ECHO "RUN AS ADMINISTRATOR"  to run this batch. Exiting... & ECHO. &
  PAUSE & EXIT /D)
REM ... proceed here with admin rights ...

这样,用户必须右键单击并选择“以管理员身份运行”。如果检测到管理员权限,脚本将在REM语句之后继续执行,否则将报错退出。如果您不需要PAUSE,只需删除它。 重要:NET文件[…]EXIT /D)必须在同一行。为了更好的可读性,它在这里以多行方式显示!


在一些机器上,我遇到了一些问题,这些问题在上面的新版本中已经解决了。一个是由于不同的双引号处理,另一个问题是由于UAC在Windows 7机器上被禁用(设置为最低级别),因此脚本一次又一次地调用自己。

我现在已经通过剥离路径中的引号并在以后重新添加它们来修复这个问题,并且我还添加了一个额外的参数,当脚本以提升的权限重新启动时添加。

双引号被以下方法删除(详细信息在这里):

setlocal DisableDelayedExpansion
set "batchPath=%~0"
setlocal EnableDelayedExpansion

然后可以使用!batchPath!访问该路径。它不包含任何双引号,因此在脚本后面使用“!batchPath!”是安全的。

这条线

if '%1'=='ELEV' (shift & goto gotPrivileges)

检查脚本是否已经被VBScript脚本调用以提升权限,从而避免无休止的递归。它使用shift删除参数。


更新:

To avoid having to register the .vbs extension in Windows 10, I have replaced the line "%temp%\OEgetPrivileges.vbs" by "%SystemRoot%\System32\WScript.exe" "%temp%\OEgetPrivileges.vbs" in the script above; also added cd /d %~dp0 as suggested by Stephen (separate answer) and by Tomáš Zato (comment) to set script directory as default. Now the script honors command line parameters being passed to it. Thanks to jxmallet, TanisDLJ and Peter Mortensen for observations and inspirations. According to Artjom B.'s hint, I analyzed it and have replaced SHIFT by SHIFT /1, which preserves the file name for the %0 parameter Added del "%temp%\OEgetPrivileges_%batchName%.vbs" to the :gotPrivileges section to clean up (as mlt suggested). Added %batchName% to avoid impact if you run different batches in parallel. Note that you need to use for to be able to take advantage of the advanced string functions, such as %%~nk, which extracts just the filename. Optimized script structure, improvements (added variable vbsGetPrivileges which is now referenced everywhere allowing to change the path or name of the file easily, only delete .vbs file if batch needed to be elevated) In some cases, a different calling syntax was required for elevation. If the script does not work, check the following parameters: set cmdInvoke=0 set winSysFolder=System32 Either change the 1st parameter to set cmdInvoke=1 and check if that already fixes the issue. It will add cmd.exe to the script performing the elevation. Or try to change the 2nd parameter to winSysFolder=Sysnative, this might help (but is in most cases not required) on 64 bit systems. (ADBailey has reported this). "Sysnative" is only required for launching 64-bit applications from a 32-bit script host (e.g. a Visual Studio build process, or script invocation from another 32-bit application). To make it more clear how the parameters are interpreted, I am displaying it now like P1=value1 P2=value2 ... P9=value9. This is especially useful if you need to enclose parameters like paths in double quotes, e.g. "C:\Program Files". If you want to debug the VBS script, you can add the //X parameter to WScript.exe as first parameter, as suggested here (it is described for CScript.exe, but works for WScript.exe too). Bugfix provided by MiguelAngelo: batchPath is now returned correctly on cmd shell. This little script test.cmd shows the difference, for those interested in the details (run it in cmd.exe, then run it via double click from Windows Explorer): @echo off setlocal set a="%~0" set b="%~dpnx0" if %a% EQU %b% echo running shell execute if not %a% EQU %b% echo running cmd shell echo a=%a%, b=%b% pause

有用的链接:

批处理文件中特殊字符的含义:引号(")、空格(!)、插入号(^)、&号(&)、其他特殊字符


我使用了Matt的精彩答案,但我发现在运行提升脚本时,Windows 7和Windows 8系统之间存在差异。

在Windows 8上提升脚本后,当前目录将被设置为c:\Windows\system32。幸运的是,有一个简单的解决方法,将当前目录更改为当前脚本的路径:

cd /d %~dp0

注意:使用cd /d确保驱动器号也被更改。

要测试这一点,可以将以下内容复制到脚本中。在两个版本上正常运行可以看到相同的结果。以Admin身份运行,看看Windows 8的区别:

@echo off
echo Current path is %cd%
echo Changing directory to the path of the current script
cd %~dp0
echo Current path is %cd%
pause

正如jcoder和Matt提到的,PowerShell使它变得很容易,甚至可以在不创建新脚本的情况下嵌入到批处理脚本中。

我修改了马特的剧本:

:: Check privileges 
net file 1>NUL 2>NUL
if not '%errorlevel%' == '0' (
    powershell Start-Process -FilePath "%0" -ArgumentList "%cd%" -verb runas >NUL 2>&1
    exit /b
)

:: Change directory with passed argument. Processes started with
:: "runas" start with forced C:\Windows\System32 workdir
cd /d %1

:: Actual work

我把这个粘贴在脚本的开头:

:: BatchGotAdmin
:-------------------------------------
REM  --> Check for permissions
>nul 2>&1 "%SYSTEMROOT%\system32\icacls.exe" "%SYSTEMROOT%\system32\config\system"

REM --> If error flag set, we do not have admin.
if '%errorlevel%' NEQ '0' (
    echo Requesting administrative privileges...
    goto UACPrompt
) else ( goto gotAdmin )

:UACPrompt
    echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
    echo args = "" >> "%temp%\getadmin.vbs"
    echo For Each strArg in WScript.Arguments >> "%temp%\getadmin.vbs"
    echo args = args ^& strArg ^& " "  >> "%temp%\getadmin.vbs"
    echo Next >> "%temp%\getadmin.vbs"
    echo UAC.ShellExecute "%~s0", args, "", "runas", 1 >> "%temp%\getadmin.vbs"

    "%temp%\getadmin.vbs" %*
    exit /B

:gotAdmin
    if exist "%temp%\getadmin.vbs" ( del "%temp%\getadmin.vbs" )
    pushd "%CD%"
    CD /D "%~dp0"
:--------------------------------------

如果不是,我使用PowerShell重新启动提升的脚本。把这些句子放在你剧本的最上面。

net file 1>nul 2>nul && goto :run || powershell -ex unrestricted -Command "Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c %~fnx0 %*'"
goto :eof
:run
:: TODO: Put code here that needs elevation

我从@Matt的答案中复制了“net name”方法。他的回答有更好的文档记录,并有错误消息等。这个版本的优势在于PowerShell已经在Windows 7及以上版本上安装并可用。没有临时VBScript (*.vbs)文件,也不需要下载工具。

只要您的PowerShell执行权限没有被锁定,这个方法应该不需要任何配置或设置就可以工作。


Matt给出了一个很好的答案,但它去掉了传递给脚本的任何参数。下面是我对参数的修改。我还在Windows 8中加入了Stephen对工作目录问题的修复。

@ECHO OFF
setlocal EnableDelayedExpansion

::net file to test privileges, 1>NUL redirects output, 2>NUL redirects errors
NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' ( goto START ) else ( goto getPrivileges ) 

:getPrivileges
if '%1'=='ELEV' ( goto START )

set "batchPath=%~f0"
set "batchArgs=ELEV"

::Add quotes to the batch path, if needed
set "script=%0"
set script=%script:"=%
IF '%0'=='!script!' ( GOTO PathQuotesDone )
    set "batchPath=""%batchPath%"""
:PathQuotesDone

::Add quotes to the arguments, if needed.
:ArgLoop
IF '%1'=='' ( GOTO EndArgLoop ) else ( GOTO AddArg )
    :AddArg
    set "arg=%1"
    set arg=%arg:"=%
    IF '%1'=='!arg!' ( GOTO NoQuotes )
        set "batchArgs=%batchArgs% "%1""
        GOTO QuotesDone
        :NoQuotes
        set "batchArgs=%batchArgs% %1"
    :QuotesDone
    shift
    GOTO ArgLoop
:EndArgLoop

::Create and run the vb script to elevate the batch file
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%temp%\OEgetPrivileges.vbs"
ECHO UAC.ShellExecute "cmd", "/c ""!batchPath! !batchArgs!""", "", "runas", 1 >> "%temp%\OEgetPrivileges.vbs"
"%temp%\OEgetPrivileges.vbs" 
exit /B

:START
::Remove the elevation tag and set the correct working directory
IF '%1'=='ELEV' ( shift /1 )
cd /d %~dp0

::Do your adminy thing here...

对于某些程序,将超级秘密的__COMPAT_LAYER环境变量设置为RunAsInvoker可以工作。看看这个:

set "__COMPAT_LAYER=RunAsInvoker"
start regedit.exe

尽管这样,将没有UAC提示用户将继续没有管理权限。


我是这样做的:

NET SESSION
IF %ERRORLEVEL% NEQ 0 GOTO ELEVATE
GOTO ADMINTASKS

:ELEVATE
CD /d %~dp0
MSHTA "javascript: var shell = new ActiveXObject('shell.application'); shell.ShellExecute('%~nx0', '', '', 'runas', 1);close();"
EXIT

:ADMINTASKS
(Do whatever you need to do here)
EXIT

这样做很简单,只使用windows默认命令。 如果你需要重新分发你的批处理文件,这是很好的。

CD /d %~dp0将当前目录设置为文件的当前目录(如果还不是,则与文件所在的驱动器无关,这要归功于/d选项)。

返回当前带扩展名的文件名(如果你不包含扩展名,并且文件夹中有一个同名的exe,它将调用exe)。

这个帖子有这么多回复,我甚至不知道我的回复是否会被看到。

无论如何,我发现这种方法比其他答案上提出的其他解决方案更简单,我希望它能帮助到一些人。


下面的解决方案是干净的,工作完美。

Download Elevate zip file from https://www.winability.com/download/Elevate.zip Inside zip you should find two files: Elevate.exe and Elevate64.exe. (The latter is a native 64-bit compilation, if you require that, although the regular 32-bit version, Elevate.exe, should work fine with both the 32- and 64-bit versions of Windows) Copy the file Elevate.exe into a folder where Windows can always find it (such as C:/Windows). Or you better you can copy in same folder where you are planning to keep your bat file. To use it in a batch file, just prepend the command you want to execute as administrator with the elevate command, like this:

提升网络启动服务…


虽然不直接适用于这个问题,但因为它需要为用户提供一些信息,当我想运行从任务调度程序提升的.bat文件时,谷歌将我带到了这里。

最简单的方法是为.bat文件创建一个快捷方式,因为对于快捷方式,您可以直接从高级属性中设置以管理员身份运行。

从任务调度程序运行快捷方式,运行提升的.bat文件。


我编写了gsudo,一个针对windows的sudo:它在当前控制台中提升(没有上下文切换到新窗口),具有凭据缓存(减少UAC弹出窗口),还提升PowerShell命令。

它允许提升需要管理权限的命令,如果您愿意,也可以提升整个批处理。只要在任何需要升高的东西之前加上gsudo。

使用gsudo提升自身的批处理文件示例:

编辑:新的一行版本,适用于任何windows语言,并避免whoami问题:

net session >nul 2>nul & net session >nul 2>nul || gsudo "%~f0" && exit /b || exit /b
:: This will run as admin ::

替代方案(原版):

@echo off
  rem Test if current context is already elevated:
  whoami /groups | findstr /b BUILTIN\Administrators | findstr /c:"Enabled group" 1> nul 2>nul && goto :isadministrator
  echo You are not admin. (yet)
  :: Use gsudo to launch this batch file elevated.
  gsudo "%~f0"
  goto end
:isadministrator
  echo You are admin.
  echo (Do admin stuff now).
:end

安装:

通过chocolatey:安装gsudo 或者scoop: scoop install gsudo 或者从github上获取:https://github.com/gerardog/gsudo

查看gsudo的操作:


使用powershell。

如果cmd文件很长,我使用第一个需要提升,然后调用一个做实际工作。

如果脚本是一个简单的命令,那么所有内容都可以放在一个cmd文件中。不要忘记在脚本文件中包含路径。

模板:

@echo off
powershell -Command "Start-Process 'cmd' -Verb RunAs -ArgumentList '/c " comands or another script.cmd go here "'"

示例1:

@echo off
powershell -Command "Start-Process 'cmd' -Verb RunAs -ArgumentList '/c "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\BIN\x.ps1"'"

示例2:

@echo off
powershell -Command "Start-Process 'cmd' -Verb RunAs -ArgumentList '/c "c:\bin\myScript.cmd"'"

试试这个:

@echo off
CLS
:init
setlocal DisableDelayedExpansion
set cmdInvoke=1
set winSysFolder=System32
set "batchPath=%~0"
for %%k in (%0) do set batchName=%%~nk
set "vbsGetPrivileges=%temp%\OEgetPriv_%batchName%.vbs"
setlocal EnableDelayedExpansion
:checkPrivileges
NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' ( goto gotPrivileges ) else ( goto getPrivileges )
:getPrivileges
if '%1'=='ELEV' (echo ELEV & shift /1 & goto gotPrivileges)
ECHO.
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%vbsGetPrivileges%"
ECHO args = "ELEV " >> "%vbsGetPrivileges%"
ECHO For Each strArg in WScript.Arguments >> "%vbsGetPrivileges%"
ECHO args = args ^& strArg ^& " "  >> "%vbsGetPrivileges%"
ECHO Next >> "%vbsGetPrivileges%"
if '%cmdInvoke%'=='1' goto InvokeCmd 
ECHO UAC.ShellExecute "!batchPath!", args, "", "runas", 1 >> "%vbsGetPrivileges%"
goto ExecElevation
:InvokeCmd
ECHO args = "/c """ + "!batchPath!" + """ " + args >> "%vbsGetPrivileges%"
ECHO UAC.ShellExecute "%SystemRoot%\%winSysFolder%\cmd.exe", args, "", "runas", 1 >> "%vbsGetPrivileges%"
:ExecElevation
"%SystemRoot%\%winSysFolder%\WScript.exe" "%vbsGetPrivileges%" %*
exit /B
:gotPrivileges
setlocal & cd /d %~dp0
if '%1'=='ELEV' (del "%vbsGetPrivileges%" 1>nul 2>nul  &  shift /1)
REM Run shell as admin (example) - put here code as you like
ECHO %batchName% Arguments: P1=%1 P2=%2 P3=%3 P4=%4 P5=%5 P6=%6 P7=%7 P8=%8 P9=%9
cmd /k

如果你需要关于批处理文件的信息,运行HTML/JS/CSS代码段:

document.getElementsByTagName(“数据”)[0]。innerHTML="ElevateBatch, version 4, release<br>Required Commands:<ul><li>CLS</li><li>SETLOCAL</li><li>SET</li><li>FOR</li><li>NET</li><li>IF</li><li>ECHO</li><li>GOTO</li><li>EXIT</li><li>DEL</li></ul>它自动提升系统,如果用户按下No,它什么也不做。<br>这个不能用来创建一个提升的资源管理器”; 数据{字体类型:arial;文字修饰:没有} <数据> < /数据>


如果你不关心参数,那么这里有一个紧凑的UAC提示脚本,只有一行长。它不会传递参数,因为没有万无一失的方法来处理所有可能的有毒字符组合。

net sess>nul 2>&1||(echo(CreateObject("Shell.Application"^).ShellExecute"%~0",,,"RunAs",1:CreateObject("Scripting.FileSystemObject"^).DeleteFile(wsh.ScriptFullName^)>"%temp%\%~nx0.vbs"&start wscript.exe "%temp%\%~nx0.vbs"&exit)

在批处理文件的@echo off下面粘贴这一行。

解释

net sess>nul 2>&1部分是用来检查海拔高度的。Net sess只是Net session的简写,Net session是一个命令,当脚本没有提升权限时返回错误代码。我从这个SO答案中得到了这个想法。这里的大多数答案都是网络文件,尽管它的工作原理是一样的。该命令快速且在许多系统上兼容。

然后使用||操作符检查错误级别。如果检查成功,它将创建并执行一个WScript,该WScript在删除自己之前重新运行原始批处理文件,但具有更高的权限。


选择

WScript文件是快速可靠的最佳方法,尽管它使用临时文件。这里有一些其他的变化和他们的缺点/优点。

PowerShell

net sess>nul 2>&1||(powershell saps '%0'-Verb RunAs&exit)

优点:

很短的。 没有临时文件。

缺点:

缓慢。PowerShell启动可能很慢。 当用户拒绝UAC提示时,喷射红色文本。PowerShell命令可以封装在try{…}catch{}来防止这种情况。

Mshta WSH脚本

net sess>nul 2>&1||(start mshta.exe vbscript:code(close(Execute("CreateObject(""Shell.Application"").ShellExecute""%~0"",,,""RunAs"",1"^)^)^)&exit)

优点:

快。 没有临时文件。

缺点:

不可靠。一些Windows 10系统会阻止脚本运行,因为Windows防御器会拦截它作为一个潜在的木马。


我最近需要一个用户友好的方法,我根据这里和其他地方的贡献者的宝贵见解想出了这个方法。只需将这一行放在.bat脚本的顶部。欢迎您的反馈。

@pushd %~dp0 & fltmc | find "." && (powershell start '%~f0' ' %*' -verb runas 2>nul && exit /b)

无畏:

@pushd %~dp0 ensures a consistant working directory relative to this batch file; supports UNC paths fltmc a native windows command that outputs an error if run unelevated | find "." makes the error prettier, and causes nothing to output when elevated && ( if we successfully got an error because we're not elevated, do this... powershell start invoke PowerShell and call the Start-Process cmdlet (start is an alias) '%~f0' pass in the full path and name of this .bat file. Single quotes allow spaces in the path/file name ' %*' pass in any and all arguments to this .bat file. Funky quoting and escape sequences probably won't work, but simple quoted strings should. The leading space is needed to prevent breaking things if no arguments are present -verb runas don't just start this process...RunAs Administrator! 2>nul discard PowerShell's unsightly error output if the UAC prompt is canceled/ignored. && if we successfully invoked ourself with PowerShell, then... NOTE: in the event we don't obtain elevation (user cancels UAC), then && allows the .bat to continue running without elevation, such that any commands that require it will fail but others will work just fine. If you want the script to simply exit instead of running unelevated, make this a single ampersand: & exit /b) exits the initial .bat processing, because we don't need it anymore; we have a new elevated process currently running our .bat. Adding /b allows cmd.exe to remain open if the .bat was started from the command line...it has no effect if the .bat was double-clicked


当一个CMD脚本需要管理员权限,并且你知道它,添加这一行到脚本的最顶部(在@ECHO OFF之后):

NET FILE > NUL 2>&1 || POWERSHELL -ex Unrestricted -Command "Start-Process -Verb RunAs -FilePath '%ComSpec%' -ArgumentList '/c \"%~fnx0\" %*'" && EXIT /b

NET FILE检查现有的Administrator权限。如果没有,PowerShell将在提升的shell中启动当前脚本(带其参数),并关闭非提升的脚本。


一行批处理用户提升(带参数)

以下是我对这个古老的批量用户提升问题的单行版本,这个问题在今天仍然是相关的。 只需将代码添加到批处理脚本的顶部,就可以开始了。

沉默

这个版本不会输出任何东西,也不会在出错时暂停执行。

@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”指令还没有被引入到批处理中。也许有一天微软会振作起来。在那之前,我们只能求助于这些黑客。


%1 start "" mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c pushd ""%~dp0"" && ""%~s0"" ::","","runas",1)(window.close)&&exit