关于windows:如何自动提升我的批处理文件,以便在需要时从UAC管理员权限请求?

How can I auto-elevate my batch file, so that it requests from UAC administrator rights if required?

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

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

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


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

查看此代码(我是受到Nironwolf在线程批处理文件中发布的代码的启发,在Windows7上"拒绝访问"?,但我已经改进了它-在我的版本中,没有创建和删除任何目录来检查管理员权限):好的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
::::::::::::::::::::::::::::::::::::::::::::
:: 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=%~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和Windows XP测试过它——它对所有人都很好。优点是,在开始点之后,您可以放置任何需要系统管理员权限的内容,例如,如果您打算为了调试目的重新安装和重新运行Windows服务(假定mypackage.msi是服务安装程序包):好的。

1
2
3
msiexec /passive /x mypackage.msi
msiexec /passive /i mypackage.msi
net start myservice

如果没有这个提升权限的脚本,UAC将向您询问三次管理员用户和密码—现在开始时只向您询问一次,并且仅在需要时才询问。好的。

如果您的脚本只需要显示一条错误消息,并在没有任何管理员特权而不是自动提升的情况下退出,则更简单:您可以通过在脚本开头添加以下内容来实现此目的:好的。

1
2
3
4
5
@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 FILE [...] EXIT /D)必须在同一行。为了提高可读性,它在这里以多行显示!好的。

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

我现在通过去掉路径中的引号并稍后重新添加来解决这个问题,并且添加了一个额外的参数,该参数在脚本以提升的权限重新启动时添加。好的。

双引号由以下内容删除(详细信息如下所示):好的。

1
2
3
setlocal DisableDelayedExpansion
set"batchPath=%~0"
setlocal EnableDelayedExpansion

然后,您可以使用!batchPath!访问路径。它不包含任何双引号,因此可以在脚本后面说"!batchPath!"。好的。

线好的。字母名称(P)Checks if the script has already been called by the VBSCRT script to promote rights,hence avoiding endless returnions.你用0音标移除了参数好的,好的。(P)Update:好的,好的。

  • (P)To avoid having to register the EDOCX1 theographic 1 original extension in windows 10,I have replaced the line
    。字母名称按字母顺序排列的字母名称字母名称
    In the script above;also added EDOCX1 commercial 4 as suggested by Stephen(separate answer)and by Tom?Zato(comment)to set script directory as default.好的,好的。
  • (P)Now the script honor command line parameters being passed to it.Thanks to JXMallet,Tanisdlj and Peter Mortensen for Observations and灵感.好的,好的。
  • (P)According to artjom B.'s hint,I analyzed it and have replaced EDOCX1 welcox0 by EDOCX1 proposal 6,which preserves the file name for the EDOCX1 singinal 7 parameter好的,好的。
  • (P)Added EDOCX1 8 commercial to the EDOCX1 plus 9 commercial section to clean up(as MLT suggested).Added EDOCX1 English 10 to avoid impact if you run different batches in parallel.Note that you need to use EDOCX1 discuss 11 niplication to be able to take advantage of the advanced string functions,such as EDOCX1 universal 12,which extracts just the filename.好的,好的。
  • (P)Optimized script structure,improvements(added variable EDOCX1 penographic 13 nabel)——which is now referenced everyywhere allowing to change the path or name of the file easily,only delete EDOCX1 penographic 1 if batch needed to be elevated好的,好的。
  • (P)在一些案件中,一个不同的群体被要求提高地位。If the script does not work,check the following parameters:字母名称字母名称Either change the 1st parameter to EDOCX1 plography 17 common and check if that already fixes the issue.It will add EDOCX1 plograph 18 to the script performing the high.他是耶稣。Or try to change the 2nd parameter to EDOCX1 individual 19,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 invitation from another 32-bit application).好的,好的。
  • (P)To make it more clear how the parameters are interpreted,I am displaying it now like EDOCX1 original 20.如果你需要将两个阵营中的类似阵营包围起来,E.G.EDOCX1就特别有用。好的,好的。

(P)用户链接:好的,好的。

  • Meaning of special characters in batch file:
    quotes(""),bang(?),Caret(,ampersand(&;),other special characters

好吧。


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

我修改了马特的剧本:

1
2
3
4
5
:checkPrivileges
NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' ( goto gotPrivileges
) else ( powershell"saps -filepath %0 -verb runas">nul 2>&1)
exit /b

不需要使用:getPrivileges标签。


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

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

1
cd /d %~dp0

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

要测试这一点,可以将以下内容复制到脚本中。在任何一个版本上正常运行,以查看相同的结果。以管理员身份运行,并查看Windows 8的不同之处:

1
2
3
4
5
6
@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


我这样做:

1
2
3
4
5
6
7
8
9
10
11
12
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选项)。

%~nx0返回扩展名为的当前文件名(如果不包括扩展名,并且文件夹中有一个同名的exe,它将调用该exe)。

这篇文章的回复太多了,我甚至不知道我的回复是否会被看到。

不管怎样,我发现这种方法比其他答案上提出的其他解决方案简单,我希望它能帮助一些人。


马特有一个很好的答案,但是它去掉了传递给脚本的任何参数。这是我的修改,保持论点。我还在Windows8中加入了Stephen对工作目录问题的修复。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@ECHO OFF
setlocal EnableDelayedExpansion

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...


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

我不知道你会如何检测它是否已经以提升的状态运行…可能只有在出现拒绝访问错误时才重新尝试使用提升的烫发?

或者,您可以简单地让xcopyreg.exe的命令始终与psexec -h一起运行,但如果最终用户每次都需要输入其密码(或者如果您在脚本中包含密码,则不安全),这会让最终用户恼火……


如果没有,我使用PowerShell重新启动提升的脚本。把这些行放在脚本的最上面。

1
2
3
4
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执行权限未被锁定,此方法应在没有任何配置或设置的情况下工作。


对于某些程序,将超级机密__COMPAT_LAYER环境变量设置为RunAsInvoker将起作用。请检查:

1
2
set"__COMPAT_LAYER=RunAsInvoker"
start regedit.exe

尽管如此,不会有UAC提示用户在没有管理权限的情况下继续。


我把这个贴在剧本的开头:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
:: 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"
:--------------------------------------


下面的解决方案是干净的,工作得很好。

  • 从https://www.winability.com/download/elevate.zip下载Elevate zip文件

  • 在zip中,您应该找到两个文件:elevate.exe和elevate64.exe。(后者是本机64位编译,如果需要的话,尽管常规的32位版本elevate.exe在32位和64位版本的Windows中都可以正常工作)

  • 将文件elevate.exe复制到Windows始终可以找到的文件夹中(例如c:/windows)。或者你最好把你的BAT文件放在同一个文件夹里。

  • 要在批处理文件中使用它,只需在要以管理员身份执行的命令前面加上elevate命令,如下所示:

  • 1
     elevate net start service ...