如何在批处理文件中请求管理员权限

225

我正在尝试为我的用户编写一个批处理文件,使他们能够在他们的Vista机器上以UAC方式运行。该文件正在重新编写他们的hosts文件,因此需要以管理员权限运行。我需要能够通过电子邮件向他们发送.bat文件的链接。期望的行为是,当他们右键单击文件并选择“打开”时,会出现一个UAC对话框,使屏幕变暗并强制他们回答是否要将应用程序授予以管理员身份运行的权限。相反,他们只看到命令行窗口上显示“访问被拒绝”的信息。

是否有不同的实现方式?


3
如果你像我一样喜欢使用PowerShell,并且偶然发现了这篇文章,请不要错过@toster-cx的一行代码。太完美了! - Michael Repucci
16个回答

433

这个脚本很好用!只需将其粘贴到批处理文件的顶部即可。如果您想查看脚本输出,请在批处理文件底部添加一个"pause"命令。

更新:此脚本现在稍微编辑,以支持命令行参数和64位操作系统。

感谢 Eneerge @ https://sites.google.com/site/eneerge/scripts/batchgotadmin

@echo off

:: BatchGotAdmin
:-------------------------------------
REM  --> Check for permissions
    IF "%PROCESSOR_ARCHITECTURE%" EQU "amd64" (
>nul 2>&1 "%SYSTEMROOT%\SysWOW64\cacls.exe" "%SYSTEMROOT%\SysWOW64\config\system"
) ELSE (
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.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"
    set params= %*
    echo UAC.ShellExecute "cmd.exe", "/c ""%~s0"" %params:"=""%", "", "runas", 1 >> "%temp%\getadmin.vbs"

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

:gotAdmin
    pushd "%CD%"
    CD /D "%~dp0"
:--------------------------------------    
    <YOUR BATCH SCRIPT HERE>

35
有时候你被迫使用肮脏的 DOS 批处理,虽然我很讨厌,但这确实很有效。干杯! - matt burns
3
FYI,这已在Windows 8嵌入式版上测试过可行。 - Robert Snyder
8
这会导致我的计算机陷入一连串对角线穿梭开关的命令窗口,唯一的停止方式是删除原始批处理文件。它在重复运行我的批处理文件并编写vbs文件。第一次需要授权,但之后就会不断循环。 - TomDestry
2
我遇到了TomDestry遇到的完全相同的问题,即无限循环和返回代码2。这是在Windows 8.1上发生的。我知道它可以在Windows 8.0上运行,并且不能确定是8.1更新还是其他原因导致了这个问题。对我起作用的解决方案是不使用cacls.exe(或icacls),而是采用: net session> nul 2>&1 如果ERRORLEVEL 1 goto UACPrompt... - crig
4
@rahuldottech - 我修复保留参数的代码存在问题,如果没有传递任何参数,params变量将未定义,因此引号加倍代码会导致括号失衡,从而转义重定向。我通过使用set params= %*而不是set params=%*来修复了代码,因此可以确保params被定义。 - dbenham
显示剩余37条评论

77

这是我一直在使用的:

@echo off
if not "%1"=="am_admin" (
    powershell -Command "Start-Process -Verb RunAs -FilePath '%0' -ArgumentList 'am_admin'"
    exit /b
)

echo main code here
pause

注意事项:

  • -Verb RunAs 标志是启用管理员权限的关键。
  • 仅在 Windows 7 和 10 上进行了测试,您可能需要调整引号。
  • 目前不支持传递参数,但您可以将更多内容添加到 -ArgumentList 中。请注意,-ArgumentList 可以接受单个字符串或字符串数组。

1
如果您知道可能有多少个参数,您可以在 am_admin 之后包含它们来传递参数。if not "%1"=="am_admin" (powershell start -verb runas '%0' 'am_admin "%~1" "%~2"' & exit) 参数将会比它们原本的位置高一个。 - Tony Brix
也可以使用 'am_admin%*' 传递所有内容,但不太适用于引号和空格 :/ 您可以在批处理中使用 shift 弹出第一个参数,从而修复除 %0 之外的所有参数。 - toster-cx
这是一个很好的答案,但不应该被接受,因为它没有检查第一次运行时是否以管理员权限运行。 - T.Todua
2
if not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit /b) 后面添加 cd /D %~dp0 以保持工作目录。 - Pedro Duarte
好的答案。它可以正常工作,而VB脚本会创建一个无限循环,即使有%temp%的写入权限。 - Twonky
太棒了。这将避免我频繁右键单击。当有大量的小批处理文件时,一行代码更受欢迎,例如仅用于启动/停止/重启服务或其集合。 (例如,交换Apache的配置以使用不同版本的PHP。) - Markus AO

20

这是我的代码!它看起来很大,但实际上大部分都是注释行(以 :: 开头的行)。

特点:

  • 完全参数转发

  • 不更改工作文件夹

  • 错误处理

  • 接受带括号的路径(除了 %TEMP% 文件夹)

  • 支持 UNC 路径

  • 映射文件夹检查(如果管理员无法访问映射驱动器,则会警告您)

  • 可用作外部库(请查看此主题中的我的帖子:https://dev59.com/4msz5IYBdhLWcg3wsaLN#30417025

  • 可以在需要时随时调用

只需将其附加到批处理文件的末尾,或将其保存为库(请参见上文)

    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:RequestAdminElevation FilePath %* || goto:eof
:: 
:: By:   Cyberponk,     v1.6 - 05/05/2023 - Fixed crash when batch filename contained spaces
::          v1.5 - 10/06/2016 - Changed the admin rights test method from cacls to fltmc
::          v1.4 - 17/05/2016 - Added instructions for arguments with ! char
::          v1.3 - 01/08/2015 - Fixed not returning to original folder after elevation successful
::          v1.2 - 30/07/2015 - Added error message when running from mapped drive
::          v1.1 - 01/06/2015
:: 
:: Func: opens an admin elevation prompt. If elevated, runs everything after the function call, with elevated rights.
:: Returns: -1 if elevation was requested
::           0 if elevation was successful
::           1 if an error occured
:: 
:: USAGE:
:: If function is copied to a batch file:
::     call :RequestAdminElevation "%~dpf0" %* || goto:eof
::
:: If called as an external library (from a separate batch file):
::     set "_DeleteOnExit=0" on Options
::     (call :RequestAdminElevation "%~dpf0" %* || goto:eof) && CD /D %CD%
::
:: If called from inside another CALL, you must set "_ThisFile=%~dpf0" at the beginning of the file
::     call :RequestAdminElevation "%_ThisFile%" %* || goto:eof
::
:: If you need to use the ! char in the arguments, the calling must be done like this, and afterwards you must use %args% to get the correct arguments:
::      set "args=%* "
::      call :RequestAdminElevation .....   use one of the above but replace the %* with %args:!={a)%
::      set "args=%args:{a)=!%" 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
setlocal ENABLEDELAYEDEXPANSION & set "_FilePath=%~1"
  if NOT EXIST "!_FilePath!" (echo/Read RequestAdminElevation usage information)
  :: UAC.ShellExecute only works with 8.3 filename, so use %~s1
  set "_FN=_%~ns1" & echo/%TEMP%| findstr /C:"(" >nul && (echo/ERROR: %%TEMP%% path can not contain parenthesis &pause &endlocal &fc;: 2>nul & goto:eof)
  :: Remove parenthesis from the temp filename
  set _FN=%_FN:(=%
  set _vbspath="%temp:~%\%_FN:)=%.vbs" & set "_batpath=%temp:~%\%_FN:)=%.bat"

  :: Test if we gave admin rights
  fltmc >nul 2>&1 || goto :_getElevation

  :: Elevation successful
  (if exist %_vbspath% ( del %_vbspath% )) & (if exist "%_batpath%" ( del "%_batpath%" )) 
  :: Set ERRORLEVEL 0, set original folder and exit
  endlocal & CD /D "%~dp1" & ver >nul & goto:eof

  :_getElevation
  echo/Requesting elevation...
  :: Try to create %_vbspath% file. If failed, exit with ERRORLEVEL 1
  echo/Set UAC = CreateObject^("Shell.Application"^) > %_vbspath% || (echo/&echo/Unable to create %_vbspath% & endlocal &md; 2>nul &goto:eof) 
  echo/UAC.ShellExecute "%_batpath%", "", "", "runas", 1 >> %_vbspath% & echo/wscript.Quit(1)>> %_vbspath%
  :: Try to create %_batpath% file. If failed, exit with ERRORLEVEL 1
  echo/@%* > "%_batpath%" || (echo/&echo/Unable to create %_batpath% & endlocal &md; 2>nul &goto:eof)
  echo/@if %%errorlevel%%==9009 (echo/^&echo/Admin user could not read the batch file. If running from a mapped drive or UNC path, check if Admin user can read it.)^&echo/^& @if %%errorlevel%% NEQ 0 pause >> "%_batpath%"

  :: Run %_vbspath%, that calls %_batpath%, that calls the original file
  %_vbspath% && (echo/&echo/Failed to run VBscript %_vbspath% &endlocal &md; 2>nul & goto:eof)
  
  :: Vbscript has been run, exit with ERRORLEVEL -1
  echo/&echo/Elevation was requested on a new CMD window &endlocal &fc;: 2>nul & goto:eof
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

如何使用示例

:EXAMPLE
@echo off

 :: Run this script with elevation
 call :RequestAdminElevation "%~dpfs0" %* || goto:eof
 
  echo/I now have Admin rights!
  echo/
  echo/Arguments using %%args%%:    %args%
  echo/Arguments using %%*: %*
  echo/%%1= %~1
  echo/%%2= %~2
  echo/%%3= %~3

  echo/
  echo/Current Directory: %CD%
  echo/
  echo/This file: %0
  echo/

pause &goto:eof

[here you paste the RequestAdminElevation function code]

你的%temp%文件夹路径中有括号吗?仅在这种情况下,应该设置错误级别1。 - cyberponk
"fc;: 2>nul" 是有意设置 ERRORLEVEL 1 以便在退出前发出错误信号。请删除 @echo off 并运行,将输出发送给我私信。谢谢! - cyberponk
无论如何,echo/%TEMP%| findstr /C:"(" >nul 测试您的 %temp% 环境变量中是否有 ( 字符,并且只有在测试结果为正时才运行 && 后面的部分。但是奇怪的是,您的 ( 测试结果却是正的。 - cyberponk
好的,看起来这个问题只会在我以管理员权限启动的控制台中运行时才会出现。以下是输出;为了简洁起见进行了清理。 call :RequestAdminElevation "...\OXYGEN1.BAT" || goto:eof setlocal ... ErrorLevel 为 0 set "_FN=_OXYGEN1" & echo/C:\Users****\AppData\Local\Temp | findstr /C:"(" 1>nul && (echo/ERROR: ... goto:eof) ErrorLevel 为 1 ... 请求在新 CMD 窗口上提升 新窗口输出与上述输出相同。 - Knyri
一旦提升权限,是否可以切换回非提升权限?因此,在.bat文件中,您将拥有以下内容: echo/I currently have non-admin/standard user rights :: Run next part with elevation call :RequestAdminElevation "%~dpfs0" %* || goto:eof echo/I now have Admin rights! :: Run next part non-elevated (ie. without elevation) call :RequestNonAdminRights "%~dpfs0" %* || goto:eof echo/I now have non-admin/standard user rights again! - DarkMatter
显示剩余9条评论

11
另一种方法是创建一个本地的快捷方式并将其设置为调用管理员权限(属性,高级,以管理员身份运行),然后向您的用户发送快捷方式(或链接到快捷方式而不是批处理文件本身)。

6

Ben Gripka的解决方案会导致无限循环。他的批处理程序工作方式如下(伪代码):

IF "no admin privileges?"
    "write a VBS that calls this batch with admin privileges"
ELSE
    "execute actual commands that require admin privileges"

正如您所看到的,如果VBS请求管理员权限失败,这将导致无限循环。
然而,即使成功请求了管理员权限,也可能会出现无限循环。
Ben Gripka批处理文件中的检查存在错误。我对该批处理进行了测试并发现,即使检查失败,管理员权限仍然可用。有趣的是,如果我从Windows资源管理器启动批处理文件,则检查按预期工作,但如果我从我的IDE启动它,则未能如此。
因此,我建议使用两个单独的批处理文件。第一个生成调用第二个批处理文件的VBS:
@echo off

echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
set params = %*:"=""
echo UAC.ShellExecute "cmd.exe", "/c ""%~dp0\my_commands.bat"" %params%", "", "runas", 1 >> "%temp%\getadmin.vbs"

"%temp%\getadmin.vbs"
del "%temp%\getadmin.vbs"

第二个文件名为“my_commands.bat”,位于与第一个相同的目录中,其中包含您实际的命令:
pushd "%CD%"
CD /D "%~dp0"
REM Your commands which require admin privileges here

这样做不会导致无限循环,还可以去除容易出错的管理员特权检查。

对你有用吗?可悲的是,对我来说,这个和所有其他来自另一个线程的东西都失败了,因为存在相同的根本问题。无论何时从另一个程序内部“间接”创建Shell对象时,“runas”参数(或命令)都会失败。感兴趣的线程在这里 - Laurie Stearn
对我有用 :) - Sritam Jagadev

6

另一种 PowerShell 解决方案...

这不是关于如何以管理员身份运行批处理脚本的问题,而是关于如何从批处理脚本中提升其他程序的权限...

我有一个批处理文件“包装器”用于一个exe文件。它们具有相同的“根文件名”,但是扩展名不同。我能够使用以下一行 PowerShell 命令以管理员身份启动 exe 文件,并将工作目录设置为包含脚本的目录:

@powershell "Start-Process -FilePath '%~n0.exe' -WorkingDirectory '%~dp0' -Verb RunAs"

更多信息

还有一系列其他可用的Start-Process选项可以应用!请查看: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-6

请注意,我使用了@前缀。这相当于在一行上使用@echo off。我在此处使用%~n0来获取批处理脚本的“根名称”,然后将.exe连接起来以指向相邻的二进制文件。使用%~dp0提供批处理所在目录的完整路径。当然,-Verb RunAs参数提供了升级权限。


4

我知道这不是对原帖的解决方案,但我确信在这里有许多其他用例,所以我想分享一下。

我遇到了所有这些答案中的代码示例问题,但后来我找到了: http://www.robotronic.de/runasspcEn.html

它不仅允许您以管理员身份运行,还会检查文件以确保其未被篡改,并将所需信息安全存储。 我承认这并不是最明显的工具,但对于我们编写代码的人来说,应该足够简单。


4
@echo off 
Net session >nul 2>&1 || (PowerShell start -verb runas '%~0' &exit /b)
Echo Administrative privileges have been got. & pause

上述方法在我的 Windows 10 版本 1903 上有效。


3

@echo offtitle可以放在这段代码之前:

net session>nul 2>&1
if %errorlevel%==0 goto main
echo CreateObject("Shell.Application").ShellExecute "%~f0", "", "", "runas">"%temp%/elevate.vbs"
"%temp%/elevate.vbs"
del "%temp%/elevate.vbs"
exit

:main
    <code goes here>
exit

许多其他答案有些累赘,如果你不需要关心以下内容:
参数
工作目录(cd %~dp0会切换到包含批处理文件的目录)

2

此外,还有来自这篇帖子的FSUTIL查询,该查询也链接到ss64.com,具有以下代码:

@Echo Off
Setlocal
:: First check if we are running As Admin/Elevated
FSUTIL dirty query %SystemDrive% >nul
if %errorlevel% EQU 0 goto START

::Create and run a temporary VBScript to elevate this batch file
   Set _batchFile=%~f0
   Set _Args=%*
   :: double up any quotes
   Set _batchFile=""%_batchFile:"=%""
   Set _Args=%_Args:"=""%

   Echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\~ElevateMe.vbs"
   Echo UAC.ShellExecute "cmd", "/c ""%_batchFile% %_Args%""", "", "runas", 1 >> "%temp%\~ElevateMe.vbs"

   cscript "%temp%\~ElevateMe.vbs" 
   Exit /B

:START
:: set the current directory to the batch file location
cd /d %~dp0
:: Place the code which requires Admin/elevation below
Echo We are now running as admin [%1] [%2]
pause

只要FSUTIL存在,它就是一个可靠的替代品。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接