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

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

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


当前回答

我使用了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

其他回答

我最近需要一个用户友好的方法,我根据这里和其他地方的贡献者的宝贵见解想出了这个方法。只需将这一行放在.bat脚本的顶部。欢迎您的反馈。

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

无畏:

@pushd %~dp0 ensures a consistant working directory relative to this batch file; supports UNC paths fltmc a native windows command that outputs an error if run unelevated | find "." makes the error prettier, and causes nothing to output when elevated && ( if we successfully got an error because we're not elevated, do this... powershell start invoke PowerShell and call the Start-Process cmdlet (start is an alias) '%~f0' pass in the full path and name of this .bat file. Single quotes allow spaces in the path/file name ' %*' pass in any and all arguments to this .bat file. Funky quoting and escape sequences probably won't work, but simple quoted strings should. The leading space is needed to prevent breaking things if no arguments are present -verb runas don't just start this process...RunAs Administrator! 2>nul discard PowerShell's unsightly error output if the UAC prompt is canceled/ignored. && if we successfully invoked ourself with PowerShell, then... NOTE: in the event we don't obtain elevation (user cancels UAC), then && allows the .bat to continue running without elevation, such that any commands that require it will fail but others will work just fine. If you want the script to simply exit instead of running unelevated, make this a single ampersand: & exit /b) exits the initial .bat processing, because we don't need it anymore; we have a new elevated process currently running our .bat. Adding /b allows cmd.exe to remain open if the .bat was started from the command line...it has no effect if the .bat was double-clicked

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

我编写了gsudo,一个针对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

安装:

通过chocolatey:安装gsudo 或者scoop: scoop install gsudo 或者从github上获取:https://github.com/gerardog/gsudo

查看gsudo的操作:

如果不是,我使用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执行权限没有被锁定,这个方法应该不需要任何配置或设置就可以工作。

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

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