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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7044985/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 08:40:22  来源:igfitidea点击:

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

windowsbatch-filewindows-10uacelevated-privileges

提问by PDixon724

I want my batch file to only run elevated. If not elevated, provide an option for the user to relaunch batch as elevated.

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

I'm writing a batch file to set a system variable, copy two files to a Program Fileslocation, and start a driver installer. If a Windows 7/Windows Vista user (UACenabled and even if they are a local admin) runs it without right-clicking and selecting "Run as Administrator", they will get 'Access Denied' copying the two files and writing the system variable.

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

I would like to use a command to automatically restart the batch as elevated if the user is in fact an administrator. Otherwise, if they are not an administrator, I want to tell them that they need administrator privileges to run the batch file. I'm using xcopyto copy the files and REG ADDto write the system variable. I'm using those commands to deal with possible Windows XP machines. I've found similar questions on this topic, but nothing that deals with relaunching a batch file as elevated.

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

采纳答案by ewall

You can have the script call itself with psexec's -hoption to run elevated.

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

I'm not sure how you would detect if it's already running as elevated or not... maybe re-try with elevated perms only if there's an Access Denied error?

我不确定您将如何检测它是否已经以提升的方式运行......也许只有在出现拒绝访问错误时才重新尝试提升权限?

Or, you could simply have the commands for the xcopyand reg.exealways be run with psexec -h, but it would be annoying for the end-user if they need to input their password each time (or insecure if you included the password in the script)...

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

回答by Matt

There is an easy way without the need to use an external tool - it runs fine with Windows 7, 8, 8.1 and 10and is backwards-compatible too (Windows XP doesn't have any UAC, thus elevation is not needed - in that case the script just proceeds).

有一种无需使用外部工具的简单方法——它在Windows 7、8、8.1 和 10 上运行良好,并且也向后兼容(Windows XP 没有任何 UAC,因此不需要提升——因为如果脚本只是继续)。

Check out this code (I was inspired by the code by NIronwolf posted in the thread Batch File - "Access Denied" On Windows 7?), but I've improved it - in my version there isn't any directory created and removed to check for administrator privileges):

查看此代码(我的灵感来自 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=%~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

The script takes advantage of the fact that NET FILErequires administrator privilege and returns errorlevel 1if you don't have it. The elevation is achieved by creating a script which re-launches the batch file to obtain privileges. This causes Windows to present the UAC dialog and asks you for the administrator account and password.

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

I have tested it with Windows 7, 8, 8.1, 10 and with Windows XP - it works fine for all. The advantage is, after the start point you can place anything that requires system administrator privileges, for example, if you intend to re-install and re-run a Windows service for debugging purposes (assumed that mypackage.msi is a service installer package):

我已经在 Windows 7、8、8.1、10 和 Windows XP 上对其进行了测试 - 它适用于所有人。好处是,在起点之后你可以放置任何需要系统管理员权限的东西,例如,如果你打算重新安装并重新运行一个 Windows 服务以进行调试(假设 mypackage.msi 是一个服务安装程序包) :

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

Without this privilege elevating script, UAC would ask you three times for your administrator user and password - now you're asked only once at the beginning, and only if required.

如果没有这个提权脚本,UAC 会问你三次管理员用户名和密码——现在你在开始时只问一次,而且只有在需要时才问。



If your script just needs to show an error message and exit if there aren't any administrator privileges instead ofauto-elevating, this is even simpler: You can achieve this by adding the following at the beginning of your script:

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

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

This way, the user has to right-click and select "Run as administrator". The script will proceed after the REMstatement if it detects administrator rights, otherwise exit with an error. If you don't require the PAUSE, just remove it. Important:NET FILE [...] EXIT /D)must be on the same line. It is displayed here in multiple lines for better readability!

这样,用户必须右键单击并选择“以管理员身份运行”REM如果检测到管理员权限,脚本将在语句之后继续执行,否则退出并出现错误。如果您不需要PAUSE,只需将其删除。 重要提示:NET FILE [...] EXIT /D)必须在同一行上。它在此处以多行显示以提高可读性!



On some machines,I've encountered issues, which are solved in the new version above already. One was due to different double quote handling, and the other issue was due to the fact that UAC was disabled (set to lowest level) on a Windows 7 machine, hence the script calls itself again and again.

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

I have fixed this now by stripping the quotes in the path and re-adding them later, and I've added an extra parameter which is added when the script re-launches with elevated rights.

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

The double quotes are removed by the following (details are here):

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

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

You can then access the path by using !batchPath!. It doesn't contain any double quotes, so it is safe to say "!batchPath!"later in the script.

然后,您可以使用 访问该路径!batchPath!。它不包含任何双引号,因此可以安全地"!batchPath!"在脚本中稍后说明。

The line

线

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

checks if the script has already been called by the VBScriptscript to elevate rights, hence avoiding endless recursions. It removes the parameter using shift.

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



Update:

更新:

  • To avoid having to register the .vbsextension 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 %~dp0as 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 SHIFTby SHIFT /1, which preserves the file name for the %0parameter

  • Added del "%temp%\OEgetPrivileges_%batchName%.vbs"to the :gotPrivilegessection to clean up (as mltsuggested). Added %batchName%to avoid impact if you run different batches in parallel. Note that you need to use forto be able to take advantage of the advanced string functions, such as %%~nk, which extracts just the filename.

  • Optimized script structure, improvements (added variable vbsGetPrivilegeswhich is now referenced everywhere allowing to change the path or name of the file easily, only delete .vbsfile 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=1and check if that already fixes the issue. It will add cmd.exeto 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 //Xparameter to WScript.exe as first parameter, as suggested here(it is described for CScript.exe, but works for WScript.exe too).

  • 为了避免必须注册.vbs在扩展的Windows 10,我已经取代了线
    "%temp%\OEgetPrivileges.vbs"
    通过
    "%SystemRoot%\System32\WScript.exe" "%temp%\OEgetPrivileges.vbs"
    在上述脚本; 还cd /d %~dp0按照斯蒂芬(单独的答案)和托马的建议添加?Zato(注释)将脚本目录设置为默认值。

  • 现在脚本尊重传递给它的命令行参数。感谢 jxmallet、TanisDLJ 和 Peter Mortensen 的观察和启发。

  • 根据Artjom B.的提示,我分析了它并替换SHIFTSHIFT /1,它保留了%0参数的文件名

  • 添加del "%temp%\OEgetPrivileges_%batchName%.vbs":gotPrivileges要清理的部分(如mlt建议的那样)。添加%batchName%以避免在并行运行不同批次时产生影响。请注意,您需要使用for才能利用高级字符串函数,例如%%~nk仅提取文件名的 。

  • 优化脚本结构,改进(添加变量vbsGetPrivileges现在随处引用,允许轻松更改文件的路径或名称,仅.vbs在需要提升批处理时删除文件)

  • 在某些情况下,提升需要不同的调用语法。如果脚本不起作用,请检查以下参数:
    set cmdInvoke=0
    set winSysFolder=System32
    将第一个参数更改为set cmdInvoke=1并检查是否已经解决了问题。它将添加cmd.exe到执行高程的脚本中。
    或者尝试将第二个参数更改为winSysFolder=Sysnative,这在 64 位系统上可能会有所帮助(但在大多数情况下不是必需的)。(ADBailey 已经报道了这一点)。只有从 32 位脚本主机(例如 Visual Studio 构建过程或从另一个 32 位应用程序调用脚本)启动 64 位应用程序时才需要“Sysnative”。

  • 为了更清楚地说明参数的解释方式,我现在将其显示为P1=value1 P2=value2 ... P9=value9. 如果您需要将参数(如路径)括在双引号中,例如"C:\Program Files".

  • 如果你想调试VBS脚本,您可以添加//X参数WScript.exe的作为第一个参数,如建议在这里(它是为CScript.exe中描述,但对于WScript.exe的作品太)。

Useful links:

有用的链接:

回答by Ir Relevant

As jcoder and Matt mentioned, PowerShell made it easy, and it could even be embedded in the batch script without creating a new script.

正如 jcoder 和 Matt 提到的,PowerShell 使它变得简单,它甚至可以嵌入到批处理脚本中,而无需创建新脚本。

I modified Matt's script:

我修改了马特的脚本:

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

回答by Stephen Klancher

I am using Matt's excellent answer, but I am seeing a difference between my Windows 7 and Windows 8 systems when running elevated scripts.

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

Once the script is elevated on Windows 8, the current directory is set to C:\Windows\system32. Fortunately, there is an easy workaround by changing the current directory to the path of the current script:

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

cd /d %~dp0

Note: Use cd /dto make sure drive letter is also changed.

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

To test this, you can copy the following to a script. Run normally on either version to see the same result. Run as Admin and see the difference in Windows 8:

要对此进行测试,您可以将以下内容复制到脚本中。在任一版本上正常运行以查看相同的结果。以管理员身份运行并查看 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

回答by Matheus Rocha

I do it this way:

我这样做:

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

This way it's simple and use only windows default commands. It's great if you need to redistribute you batch file.

这种方式很简单,只使用 Windows 默认命令。如果您需要重新分发批处理文件,那就太好了。

CD /d %~dp0Sets the current directory to the file's current directory (if it is not already, regardless of the drive the file is in, thanks to the /doption).

CD /d %~dp0将当前目录设置为文件的当前目录(如果它还没有,无论文件在哪个驱动器中,感谢/d选项)。

%~nx0Returns the current filename with extension (If you don't include the extension and there is an exe with the same name on the folder, it will call the exe).

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

There are so many replies on this post I don't even know if my reply will be seen.

这个帖子的回复太多了,我都不知道我的回复会不会被人看到。

Anyway, I find this way simpler than the other solutions proposed on the other answers, I hope it helps someone.

无论如何,我发现这种方式比其他答案中提出的其他解决方案更简单,我希望它可以帮助某人。

回答by jxmallett

Matt has a great answer, but it strips away any arguments passed to the script. Here is my modification that keeps arguments. I also incorporated Stephen's fix for the working directory problem in Windows 8.

Matt 有一个很好的答案,但它去掉了传递给脚本的任何参数。这是我保留参数的修改。我还结合了斯蒂芬对 Windows 8 中工作目录问题的修复。

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

回答by Ryan Bemrose

I use PowerShell to re-launch the script elevated if it's not. Put these lines at the very top of your script.

如果不是,我使用 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

I copied the 'net name' method from @Matt's answer. His answer is much better documented and has error messages and the like. This one has the advantage that PowerShell is already installed and available on Windows 7 and up. No temporary VBScript (*.vbs) files, and you don't have to download tools.

我从@Matt 的回答中复制了“网络名称”方法。他的回答记录得更好,并且有错误消息等。这样做的优点是 PowerShell 已经安装并可以在 Windows 7 及更高版本上使用。没有临时 VBScript (*.vbs) 文件,您也不必下载工具。

This method should work without any configuration or setup, as long as your PowerShell execution permissions aren't locked down.

只要您的 PowerShell 执行权限没有被锁定,此方法应该可以在没有任何配置或设置的情况下工作。

回答by npocmaka

For some programs setting the super secret __COMPAT_LAYERenvironment variable to RunAsInvokerwill work.Check this :

对于某些设置超级秘密__COMPAT_LAYER环境变量的程序RunAsInvoker将起作用。检查这个:

set "__COMPAT_LAYER=RunAsInvoker"
start regedit.exe

Though like this there will be no UAC prompting the user will continue without admin permissions.

虽然像这样不会有 UAC 提示用户将在没有管理员权限的情况下继续。

回答by TanisDLJ

I pasted this in the beginning of the script:

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

:: 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"
:--------------------------------------

回答by Gerardo Grignoli

It's 2020 and why windows does not have a sudo tool yet?

现在是 2020 年,为什么 windows 还没有 sudo 工具?

So, I wrote gsudo, a sudofor windows: it elevates in the current console(no context switching to a new window), with a credentials cache (reduced UAC popups), and also elevates PowerShell commands.

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

It allows to elevate commands that require admin privileges, or the whole batch, if you want:

如果需要,它允许提升需要管理员权限或整个批处理的命令:

Just prepend gsudobefore anything that needs to run elevated.

只需gsudo在需要提升的任何内容之前添加。

Example batch file that elevates itself:

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

@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

See gsudo in action: gsudo demo

参见 gsudo 操作: gsudo 演示

Install:

安装: