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

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

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

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

353464 次浏览

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

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

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

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

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

::::::::::::::::::::::::::::::::::::::::::::
:: 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 FILE [...] EXIT /D)必须在同一行。为了更好的可读性,它在这里以多行方式显示!


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

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

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

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

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

这条线

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

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


更新:

    为了避免必须在Windows 10中注册.vbs扩展名,我替换了
    这一行 "%temp%\OEgetPrivileges.vbs"
    ,
    "%SystemRoot%\System32\WScript.exe" "%temp%\OEgetPrivileges.vbs"
    在上面的脚本;还根据Stephen(单独回答)和Tomáš Zato(评论)的建议添加了cd /d %~dp0,以设置脚本目录为默认值
  • 现在脚本尊重传递给它的命令行参数。感谢jxmallet、TanisDLJ和Peter Mortensen的观察和启发。

  • 根据Artjom B。的提示,我分析了它,并用SHIFT /1替换了SHIFT,这保留了%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对此进行了报道)。“Sysnative"只有从32位脚本主机启动64位应用程序(例如Visual Studio构建过程,或从另一个32位应用程序调用脚本)时才需要。

  • 为了更清楚地说明参数是如何解释的,我现在以P1=value1 P2=value2 ... P9=value9的方式显示它。如果你需要用双引号括起参数,如路径,这尤其有用,例如"C:\Program Files"

  • 如果你想调试VBS脚本,你可以将//X参数作为第一个参数添加到WScript.exe中,如在这里所示(它用于CScript.exe,但也适用于WScript.exe)。

  • MiguelAngelo: batchPath提供的错误修正现在在cmd shell上正确返回。这个小脚本test.cmd显示了区别,对于那些对细节感兴趣的人(在cmd.exe中运行它,然后在Windows资源管理器中双击运行它):

    @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选项)。

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

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

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

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

  1. 下载Elevate zip文件from https://www.winability.com/download/Elevate.zip

  2. 在zip中,你会发现两个文件:Elevate64.exe和Elevate64.exe。(如果您需要的话,后者是原生64位编译,不过常规的32位版本elevation .exe应该可以在32位和64位版本的Windows上正常运行)

  3. 将文件Elevate.exe复制到Windows总能找到它的文件夹(例如C:/Windows)。或者你最好复制到你打算保存bat文件的文件夹中。

  4. 要在批处理文件中使用它,只需在你想以管理员身份执行的命令前加上elevate命令,就像这样:

 elevate net start service ...

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

最简单的方法是为.bat文件创建一个快捷方式,因为对于快捷方式,可以直接从高级属性中设置Run as administrator

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

我写了gsudoa用于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

安装:

见gsudo in action: gsudo demo

使用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("data")[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>It auto-elevates the system and if the user presses No, it just doesn't do anything.<br>This CANNOT be used to create an Elevated Explorer.";
data{font-family:arial;text-decoration:none}
<data></data>

如果你不关心参数,那么这里有一个紧凑的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的缩写,它是一个命令,当脚本没有提升权限时返回错误代码。我从这个SO答案得到这个想法。这里的大多数答案都是net file,尽管它的工作原理相同。该命令快速且在许多系统上兼容。

然后用||操作符检查错误级别。如果检查成功,它将创建并执行一个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确保相对于这个批处理文件有一个一致的工作目录;支持UNC路径
  • fltmc本机windows命令,如果未提升运行则输出错误
  • | find "."使错误更漂亮,并且在提升时不会导致任何输出
  • && (如果我们成功因为我们没有被提升而出现错误,执行这个…
  • powershell start调用PowerShell并调用Start-Process cmdlet (开始是别名)
  • '%~f0'传递这个.bat文件的完整路径和名称。单引号允许在路径/文件名中使用空格
  • ' %*'将任何和所有参数传递给这个.bat文件。时髦的引号和转义序列可能不起作用,但简单的引号字符串应该起作用。如果没有参数,需要前导空格来防止破坏
  • -verb runas不只是启动这个进程
  • 2>nul丢弃PowerShell的难看的错误输出,如果UAC提示被取消/忽略。
  • &&如果我们用PowerShell调用自己,那么…
    • 注意:如果我们没有获得仰角(用户取消UAC),那么&& 允许 .bat在没有仰角的情况下继续运行,这样任何需要它的命令都会失败,但其他命令会正常工作。如果你想让脚本简单地退出,而不是不提升运行,请将其设置为单个&号:&
  • exit /b)退出初始的.bat处理,因为我们不再需要它了;我们有一个新的提升进程正在运行我们的.bat。如果.bat是从命令行启动的,则添加/b允许cmd.exe保持打开状态…如果双击.bat,它将不起作用

当你知道一个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

操作方法

  1. 使用fltmc检查管理员权限。(系统组件,包括在windows2000 +内)
  2. 如果用户已具有管理员权限,则继续正常操作。
  3. 如果不是,使用任意一种方法生成一个自身的提升版本:
    1. powershell (可选的Windows功能,默认包含在Windows 7+中,可以卸载/否则不可用,可以安装在Windows XP/Vista上)
    2. mshta (系统组件,包括在windows2000 +内)
  4. 如果获取提升失败,则停止执行(而不是无休止地循环)。

这个解决方案与其他解决方案的区别是什么?

实际上,解决这个问题的方法有数百种,但我迄今为止发现的所有方法都有其缺点,这是解决其中大多数问题的尝试。

  • 兼容性。使用fltmc检查特权,使用powershell或mshta检查提升,适用于2000年以来的每个Windows版本,并且应该涵盖大多数系统配置。
  • 不写入任何额外的文件。
  • 保存当前工作目录。发现的大多数解决方案都合并了“脚本目录”;使用“工作目录”;这是完全不同的概念。如果你想使用“脚本目录”;相反,用%~dp0替换%cd%。有些人主张使用pushd "%~dp0"来代替,这样网络UNC路径中的路径就像"\\ persons - pc \share"将工作,但这也会自动映射到一个驱动器号(如Y:),这可能是或可能不是你想要的。
  • 如果无法获得高度则停止。出现这种情况的原因有很多,比如用户点击“不”;在UAC提示符,UAC被禁用,组策略设置等。许多其他解决方案在这一点上进入了一个无休止的循环,产生了数百万个命令提示,直到宇宙热死。
  • 支持(大多数)命令行参数和奇怪的路径。比如& &,百分号%,插入号^,以及引号“;""”的不匹配数量。你仍然可以通过传递这些足够奇怪的组合来打破这个问题,但这是Windows批处理的固有缺陷,不能真正解决任何组合。大多数典型的用例应该包括在内,并且参数在没有提升脚本的情况下也能正常工作。

已知的问题

如果输入的命令行参数的双引号数量不匹配(即不能被2整除),则会添加一个额外的空格和插入号^作为最后一个参数。例如,"arg1" arg2" """" "arg3"将变成"arg1" arg2" """" "arg3" ^。如果这对您的脚本很重要,您可以添加逻辑来修复它,f.ex。检查_ELEV=1(意味着需要抬高),然后检查参数列表的最后一个字符是否为^和/或引号的数量不匹配,并删除行为不当的插入符号。

日志输出到文件的示例脚本

你不能轻易使用>对于标准输出日志记录,因为在提升中产生了一个新的CMD窗口,并切换了执行上下文。

可以通过传递越来越奇怪的转义字符组合来实现它,比如elevate.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
< p >运行:logtest.bat -log 通过添加参数-log,输出将被记录到一个文件中,而不是stdout

封闭的思想

让我困惑的是,一个简单的“ELEVATE"即使在UAC存在15年后,指令也没有被引入到批处理中。也许有一天微软会振作起来。在此之前,我们必须使用这些黑客

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