ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

查看我发布的内容 - 您不需要任何外部工具,脚本会自动检查管理员权限并在需要时自动提升自身。
请考虑一下马特的答案是否会被勾选?在我看来是这样。
请注意我发布的 batch script 的评论部分中的新 Windows 10 提示。
从 cmd:@powershell Start-Process cmd -Verb runas。从 Powershell 只需删除 @powershell。这以提升的权限启动 cmd。

M
Matt

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

查看此代码(我受到 NIronwolf 在线程 Batch File - "Access Denied" On 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 会向您询问 3 次管理员用户名和密码 - 现在您在开始时只被询问一次,并且仅在需要时询问您。

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

@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 机器上被禁用(设置为最低级别),因此脚本一次又一次地调用自身。

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

双引号被以下内容删除(详细信息为 here):

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

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

线

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

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

更新:

为避免必须在 Windows 10 中注册 .vbs 扩展名,我在上面的脚本;还按照 Stephen(单独的答案)和 Tomáš Zato(评论)的建议添加了 cd /d %~dp0 以将脚本目录设置为默认值。

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

根据 Artjom B. 的提示,我对其进行了分析并将 SHIFT 替换为 SHIFT /1,它保留了 %0 参数的文件名

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

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

在某些情况下,提升需要不同的调用语法。如果脚本不起作用,请检查以下参数: set cmdInvoke=0 set winSysFolder=System32 将第一个参数更改为设置 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 if not %a% EQU %b% echo running cmd shell echo a=%a%, b=%b% pause

有用的链接:

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


很好的答案,尽管在某些情况下你必须做所有这些事情来做一些显然是必要的事情,这让我有点惊讶。
实际上,Windows 批处理语言中明显缺少诸如 ELEVATE 之类的命令。
也许使用 powershell 更容易,这似乎是更复杂的事情的批准脚本语言,但到目前为止我从来没有费心去学习它:(
powershell 中的语法完全不同(动词-名词语法),但它允许您轻松调用 .NET 程序集。但是处理起来有点困难(签署脚本等)。
@Matt 您能否检查一下您的答案 this rejected edit 背后是否有一些真相?
c
ceztko

正如 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

没错,如果安装了 PowerShell,您可以使用它来运行带有提升的批处理文件(感谢您提供代码片段!)。是的,不需要标签。感谢您的提示,值得+1 ... :-)
从 cmd Powershell.exe 调用时,没有 -verb runas 选项。如果您已经在 PowerShell 中,它确实存在。
我真的很喜欢这个解决方案,非常适合我。在 Windows 8.1 上,我确实需要 :gotPrivileges 标签才能工作。
如果这个批处理文件在 UNC 路径上是远程的,我会遇到问题。
我在开始时添加了更改目录,简化了流程并进行了一些清理。由于runas 进程中的安全原因,无法通过 Start-Process 中的 WorkingDirectory 参数更改工作目录
M
Matheus Rocha

我这样做:

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)。

这个帖子回复太多了,不知道能不能看到我的回复。

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


cd %~dp0 在网络驱动器上不起作用,请改用 pushd %~dp0。
我喜欢这个答案的简单性。最后是使用 Jscript 的理由!
@Wolf我输入了一个巨大的答案才意识到我误解了你......我现在明白你的意思了。我将对其进行编辑以包含 /d。谢谢朋友 :) (PS:钢琴家也在这里!)
我尝试了 Stavm 的评论,但 pushd 并没有太大帮助。所做的是将批处理文件的 MSHTA 行(我放在 pushd %~dp0%popd 之间)替换为以下 3 行:SET DPZERO=%~dp0%SET NXZERO=%~nx0% 和(其余部分,通过引号,是一条长线)MSHTA "javascript: var oShellApp = new ActiveXObject('Shell.Application'); var oWSH = new ActiveXObject('WScript.Shell'); oShellApp.ShellExecute('CMD', '/C ' + oWSH.Environment("Process")("DPZERO") + oWSH.Environment("Process")("NXZERO"), '', 'runas', 1);close();"
有趣,但 Windows Defender 将其检测为特洛伊木马并在启动时将其隔离...
W
Wolf

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

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

cd /d %~dp0

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

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

很好的提示,斯蒂芬。所以脚本应该以 cd %~dp0 结尾以保留它的当前路径(我假设这在 Win7 中也适用,因此可以使用相同的命令,尽管只在 Win8+ 中需要)。为此+1!
值得注意的是,这在我运行 Windows 7 的系统上也是必需的。
或使用 pushd %~dp0 代替...为什么?因为popd
i
isidroco

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

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

这是一个有用的答案。但是在 ECHO UAC.ShellExecute.... 行,"batchArgs!" 不会扩展变量。使用 "!batchArgs!"。我的编辑被拒绝了,所以我发表评论。
@fliedonion 很好发现!我不确定为什么您的编辑被拒绝,因为这绝对是一个错字并且您的修复工作有效。自己进行了更改并在 Win 8.1 上进行了测试。现在找到我使用此代码的所有脚本....
使用带引号的参数(如 test.bat "a thing""test script.bat" arg1 arg2)时脚本中断。现在都修好了。
我设法打破了它(由于我的错误:从映射的网络驱动器运行脚本,因为管理员和普通用户没有相同的映射)。仍然:有没有办法查看输出?对我来说,我必须找到 .vbs 并将 /c 更改为 /K,然后手动查看。
@jxmallet - 只需将 '!arg!' 替换为 '%arg%' 即可使用双引号参数。这项工作对我来说......
e
ewall

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

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

或者,您可以简单地让 xcopyreg.exe 的命令始终与 psexec -h 一起运行,但如果最终用户每次都需要输入密码(或者如果您包括脚本中的密码)...


感谢您的回复。不幸的是,我认为我不能使用任何现有的 Windows Vista/7 工具以外的工具,因为这将面向我办公室以外的客户。我认为我不能合法地分发 PSExec。
是的,我认为你是对的——尽管 PSExec 现在是 Microsoft 工具(因为他们买断了 Sysinternals 的人!) EULA 确实禁止分发 :(
我认为我的选择非常有限。如果我知道如何在 VB 中编写代码,我可以将其制作为带有管理员清单的 exe,但我什至不知道从哪里开始。我想如果他们运行的是 Windows Vista/7,我会在批处理开始时警告以管理员身份运行。谢谢大家。
另一个可以自由再分发且易于集成和学习的第 3 方工具是 AutoItthis page 演示了脚本如何请求提升的权限。
psexec -h 不起作用:“无法安装 PSEXESVC 服务:访问被拒绝。”。您需要已经拥有管理员权限才能运行 psexec。
P
Peter Mortensen

如果不是,我使用 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 的回答中复制了“网名”方法。他的答案记录得更好,并且有错误消息等。这样做的好处是 PowerShell 已在 Windows 7 及更高版本上安装并可用。没有临时 VBScript (*.vbs) 文件,您也不必下载工具。

只要您的 PowerShell 执行权限未锁定,此方法无需任何配置或设置即可工作。


当提供一个用引号括起来的列表空格分隔参数以将其视为一个时,/c %~fnx0 %*' 部分似乎将除第一个部分之外的所有部分都留下。例如,来自 test.bat "arg1 arg2 arg3" 的只有 arg1 向前传递
似乎无论如何, Start-Process 都会删除参数列表中的所有双引号。
为我工作!我将它用于 netsh int set int %wifiname% Enable/Disable
我和上面的 Nicolas Mommaerts 有同样的问题。我不明白为什么它会起作用(最好的修复方法),但下面的工作应该是这样的(注意最后所有额外的引号):net file 1>nul 2>nul && goto :run || powershell -ex unrestricted -Command "Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c ""%~fnx0""""'"
另一个问题(不便)是如果服务器服务未运行(我出于某种原因将其禁用),脚本会暂停(等待用户输入)。我通常通过尝试写入 HOSTS 文件位置来测试管理员权限,但最好调用 powershell 命令 -Verb runas,而不是依赖启用的 Windows 服务或尝试文件写入。
C
Community

对于某些将超级机密 __COMPAT_LAYER 环境变量设置为 RunAsInvoker will work 的程序。检查:

set "__COMPAT_LAYER=RunAsInvoker"
start regedit.exe

虽然这样不会有 UAC 提示用户将继续没有管理员权限。


指向 Microsoft 站点的链接不再有效,因此我将其更改为 archive.org 上的内容。如果有人可以在 Microsoft 网站上找到指向该内容的有效链接,但无论如何,请将其更新为该链接。
它仍处于中等完整性而不是提升,因此您无法在 regedit 中编辑 HKLM。它只是跳过了uac。
G
Gerardo Grignoli

我写了 gsudoa sudo for windows:在 当前控制台 中提升(没有上下文切换到新窗口),带有凭据缓存(减少 UAC弹出窗口),还提升了 PowerShell 命令

如果需要,它允许提升需要管理员权限的命令或整个批处理。只需在需要运行提升的任何内容之前添加 gsudo

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

编辑:新的一个班轮版本,适用于任何 Windows 语言并避免 whoami issues

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:choco 安装 gsudo

或舀:舀安装 gsudo

或从 github 获取:https://github.com/gerardog/gsudo

https://raw.githubusercontent.com/gerardog/gsudo/master/demo.gif


凉爽的。谢谢。它在哪些版本的 Windows 上运行?
测试套件在 Server 2019 上运行,我的本地开发盒是 Windows 10 1909。抱歉,我没有测试与其他旧操作系统的兼容性。
谢谢杰拉尔多。如果有人在其他版本的 Windows 上尝试过,请发布您的结果。
我刚刚在 Windows 8.1 上测试了 gsudo v0.7 并且工作正常。在那里安装 .Net Framework 4.6 并非易事。幸运的是 choco install dotnet4.6.2 带来了一些难以获得的依赖项。然后 gsudo config Prompt "$p# " 修复了 ConHost 显示错误提示(转义序列字符而不是红色尖角)的问题。 @RockPaperLizard
非常感谢杰拉尔多。它是否可以重新编译以与 4.6 之前的任何版本的 .Net Framework 一起使用,或者它是否依赖于 4.6 特定的某些功能?
J
Jamesfo

我最近需要一种用户友好的方法,我根据此处和elsewhere的贡献者的宝贵见解提出了这个方法。只需将此行放在 .bat 脚本的顶部即可。欢迎反馈。

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

解释:

@pushd %~dp0 确保与此批处理文件相关的工作目录一致;支持 UNC 路径

fltmc 一个本机 Windows 命令,如果运行未提升,则输出错误

|寻找 ”。”使错误更漂亮,并且在提升时不会导致任何输出

&& (如果我们因为没有被提升而成功出错,请执行此操作...

powershell start 调用 PowerShell 并调用 Start-Process cmdlet(start 是别名)

'%~f0' 传入此 .bat 文件的完整路径和名称。单引号允许路径/文件名中有空格

'%*' 将任何和所有参数传递给此 .bat 文件。时髦的引用和转义序列可能不起作用,但简单的引用字符串应该。如果不存在参数,则需要前导空格以防止破坏事物

-verb runas 不只是启动这个过程...以管理员身份运行!

2>nul 如果取消/忽略 UAC 提示,则丢弃 PowerShell 难看的错误输出。

&& 如果我们成功地使用 PowerShell 调用了自己,那么... 注意:如果我们没有获得提升(用户取消 UAC),那么 && 允许 .bat 继续运行而无需提升,这样任何需要它的命令都会失败,但其他人会正常工作。如果您希望脚本简单地退出而不是运行未提升,请将其设为单个 & 符号:&

注意:如果我们没有获得提升(用户取消 UAC),则 && 允许 .bat 在没有提升的情况下继续运行,这样任何需要它的命令都会失败,但其他命令会正常工作。如果您希望脚本简单地退出而不是运行未提升,请将其设为单个 & 符号:&

exit /b) 退出初始的 .bat 处理,因为我们不再需要它;我们有一个新的提升进程当前正在运行我们的 .bat。如果 .bat 是从命令行启动的,添加 /b 允许 cmd.exe 保持打开状态...如果双击 .bat 则无效


做得很好!很高兴看到这个问题的好答案仍在涌现。
似乎无法按原样与我一起工作,但我可以更改 .到:在查找中,因为显然德语中的错误消息没有点。
我建议将 ... && exit /b) 更改为 ... && popd && exit /b)。这样当前目录在运行脚本后保持不变。
P
Pyprohly

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

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

备择方案

WScript 文件是快速可靠的最佳方法,尽管它使用临时文件。以下是一些其他变体及其缺点/优点。

电源外壳

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 Defender 将其作为潜在的木马拦截,某些 Windows 10 系统会阻止该脚本运行。


这条线很神奇,你能解释一下第一节 net sess>nul 2>&1 吗?
不错的解决方案,但关于第一种方法,当我尝试将任何参数传递给我的批处理文件时,它不会到达那个批处理文件....我尝试使用 ShellExecute"%fullPathArgs%" 但没有工作任何一个。看来我必须在另一个参数中传递批处理文件参数。
@Abdulhameed 如果参数很简单,您可以使用 ShellExecute"%~0","%~*",,"RunAs" 而不是 ShellExecute"%~0",,,"RunAs",但涉及引用、转义序列或毒字符的复杂参数会破坏它。请注意,其他答案也会有类似的警告。可靠地保存论点并非易事。
T
TanisDLJ

我将其粘贴在脚本的开头:

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

我喜欢你脚本中的 arg 处理。但注意 cacls 在 Windows 7 和更新的 Windows 版本中已弃用。
Fsutil dirty 更好
你知道行 pushd "%CD%" 保存当前工作目录并更改到当前工作目录吗?
H
Hugo Delsing

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

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

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


M
Manuel Alves

使用 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"'"

旁注:您不需要通过 cmd 进行代理。您可以将可执行文件放在那里,并且只在最后放置参数,如 - powershell -Command "Start-Process 'docker-compose' -Verb RunAs -ArgumentList '"-f ./build/postgres.yaml up"'"
M
Michel de Ruiter

当 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 检查现有管理员权限。如果没有,PowerShell 会在 elevated shell 中重新启动当前脚本(及其参数),然后关闭未提升的脚本。


不错的技术!赞成。如果没有授予管理员权限,这会产生无限循环吗?
不,脚本只会(给出错误并)尝试不提升。
a
anzz1

单行批量用户提升(带参数)

对于这个古老的批量用户提升问题,这是我的单行版本,今天仍然适用。只需将代码添加到批处理脚本的顶部即可。

沉默的

此版本不会输出任何内容,也不会在出错时暂停执行。

@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

操作方法

使用 fltmc 检查管理员权限。 (系统组件,包含在 Windows 2000+ 中) 如果用户已经拥有管理员权限,则继续正常操作。如果不是,则使用以下任一方法生成自身的升级版本: powershell(可选的 Windows 功能,默认包含在 Windows 7+ 中,可以卸载/否则不可用,可以安装在 Windows XP/Vista 上) mshta(系统组件,包含在Windows 2000+)如果未能获得提升,停止执行(而不是无限循环)。

该解决方案与其他解决方案有何不同?

解决这个问题实际上有数百种变体,但到目前为止我发现的所有东西都有它们的缺点,这是解决大多数问题的尝试。

兼容性。使用 fltmc 作为检查权限的方法,使用 powershell 或 mshta 进行提升适用于自 2000 年以来的每个 Windows 版本,并且应该涵盖大多数系统配置。

不写入任何额外文件。

保留当前工作目录。发现的大多数解决方案将“脚本目录”与“工作目录”混为一谈,这是完全不同的概念。如果要改用“脚本目录”,请将 %cd% 替换为 %~dp0。有些人提倡使用 pushd "%~dp0" 代替,这样网络 UNC 路径中的路径(如 "\\SOMEONES-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

运行:logtest.bat -log 通过添加参数 -log ,输出将记录到文件而不是标准输出。

结束的想法

让我感到困惑的是,即使在 UAC 存在 15 年之后,如何也没有将简单的“ELEVATE”指令引入批处理。也许有一天微软会把他们的狗屎收拾起来。在那之前,我们不得不求助于这些黑客。


佚名

尝试这个:

@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,版本 4,发布
必需命令:

  • CLS
  • SETLOCAL
  • SET
  • FOR
  • NET
  • IF
  • ECHO
  • GOTO
  • EXIT
  • li>
  • DEL
它会自动提升系统,如果用户按否,它什么也不做。
这不能用于创建提升的资源管理器。";数据{font-family:arial;text-decoration:none}


V
Vaibhav Jain

以下解决方案很干净并且效果很好。

从 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 命令,如下所示:

提升网络启动服务...


该命令仅打开一个对话框窗口,询问用户是否允许执行此命令。所以你不能在没有用户交互的情况下在批处理文件中使用这个命令。
如果可以在没有用户交互的情况下进行提升,那将是一个巨大的安全漏洞,不是吗?每个病毒都会在没有用户批准的情况下开始使用这种方法来提升自己。