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

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

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


当前回答

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

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

其他回答

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

然后使用||操作符检查错误级别。如果检查成功,它将创建并执行一个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防御器会拦截它作为一个潜在的木马。

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

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

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

使用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重新启动提升的脚本。把这些句子放在你剧本的最上面。

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