我希望我的批处理文件只运行提升。如果未提升,请为用户提供一个选项以将批处理重新启动为提升。
我正在编写一个批处理文件来设置系统变量,将两个文件复制到 Program Files 位置,然后启动驱动程序安装程序。如果 Windows 7/Windows Vista 用户(已启用 UAC,即使他们是本地管理员)在没有右键单击并选择“以管理员身份运行”的情况下运行它,他们将获得“拒绝访问”复制这两个文件和写系统变量。
如果用户实际上是管理员,我想使用命令自动重新启动批处理。否则,如果他们不是管理员,我想告诉他们他们需要管理员权限才能运行批处理文件。我正在使用 xcopy 复制文件并使用 REG ADD 来写入系统变量。我正在使用这些命令来处理可能的 Windows XP 机器。我在这个主题上发现了类似的问题,但没有涉及将批处理文件重新启动为提升的问题。
@powershell Start-Process cmd -Verb runas
。从 Powershell 只需删除 @powershell
。这以提升的权限启动 cmd。
有一种无需使用外部工具的简单方法 - 它在 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 (!)、插入符号 (^)、与号 (&)、其他特殊字符
正如 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
runas
进程中的安全原因,无法通过 Start-Process
中的 WorkingDirectory
参数更改工作目录
我这样做:
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)。
这个帖子回复太多了,不知道能不能看到我的回复。
无论如何,我发现这种方式比其他答案中提出的其他解决方案更简单,我希望它对某人有所帮助。
/d
。谢谢朋友 :) (PS:钢琴家也在这里!)
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();"
我正在使用 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!
pushd %~dp0
代替...为什么?因为popd
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!"
。我的编辑被拒绝了,所以我发表评论。
test.bat "a thing"
或 "test script.bat" arg1 arg2
)时脚本中断。现在都修好了。
'!arg!'
替换为 '%arg%'
即可使用双引号参数。这项工作对我来说......
您可以使用 psexec 的 -h
选项让脚本自行调用以运行提升。
我不确定您将如何检测它是否已经以提升的方式运行......也许只有在出现“拒绝访问”错误时才使用提升的权限重试?
或者,您可以简单地让 xcopy
和 reg.exe
的命令始终与 psexec -h
一起运行,但如果最终用户每次都需要输入密码(或者如果您包括脚本中的密码)...
psexec -h
不起作用:“无法安装 PSEXESVC 服务:访问被拒绝。”。您需要已经拥有管理员权限才能运行 psexec。
如果不是,我使用 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 向前传递
netsh int set int %wifiname% Enable/Disable
。
net file 1>nul 2>nul && goto :run || powershell -ex unrestricted -Command "Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c ""%~fnx0""""'"
对于某些将超级机密 __COMPAT_LAYER
环境变量设置为 RunAsInvoker
will work 的程序。检查:
set "__COMPAT_LAYER=RunAsInvoker"
start regedit.exe
虽然这样不会有 UAC 提示用户将继续没有管理员权限。
regedit
中编辑 HKLM。它只是跳过了uac。
我写了 gsudo
,a 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
.Net Framework 4.6
并非易事。幸运的是 choco install dotnet4.6.2
带来了一些难以获得的依赖项。然后 gsudo config Prompt "$p# "
修复了 ConHost
显示错误提示(转义序列字符而不是红色尖角)的问题。 @RockPaperLizard
我最近需要一种用户友好的方法,我根据此处和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)
。这样当前目录在运行脚本后保持不变。
如果您不关心参数,那么这里有一个紧凑的 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 系统会阻止该脚本运行。
ShellExecute"%~0","%~*",,"RunAs"
而不是 ShellExecute"%~0",,,"RunAs"
,但涉及引用、转义序列或毒字符的复杂参数会破坏它。请注意,其他答案也会有类似的警告。可靠地保存论点并非易事。
我将其粘贴在脚本的开头:
:: 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"
:--------------------------------------
cacls
在 Windows 7 和更新的 Windows 版本中已弃用。
pushd "%CD%"
保存当前工作目录并更改到当前工作目录吗?
虽然不直接适用于这个问题,但因为它想为用户提供一些信息,当我想运行从任务调度程序提升的 .bat 文件时,谷歌把我带到了这里。
最简单的方法是创建 .bat 文件的快捷方式,因为对于快捷方式,您可以直接从高级属性中设置 Run as administrator
。
从任务计划程序运行快捷方式,运行提升的 .bat 文件。
使用 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"'"
powershell -Command "Start-Process 'docker-compose' -Verb RunAs -ArgumentList '"-f ./build/postgres.yaml up"'"
当 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 中重新启动当前脚本(及其参数),然后关闭未提升的脚本。
单行批量用户提升(带参数)
对于这个古老的批量用户提升问题,这是我的单行版本,今天仍然适用。只需将代码添加到批处理脚本的顶部即可。
沉默的
此版本不会输出任何内容,也不会在出错时暂停执行。
@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,发布
必需命令:
以下解决方案很干净并且效果很好。
从 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 命令,如下所示:
提升网络启动服务...
不定期副业成功案例分享