¿Cómo puedo elevar automáticamente mi archivo por lotes, para que solicite derechos de administrador de UAC si es necesario?


Quiero que mi archivo por lotes solo se ejecute con valores elevados. Si no es elevado, proporcione una opción para que el usuario vuelva a iniciar batch como elevado.

Estoy escribiendo un archivo por lotes para establecer una variable del sistema, copiar dos archivos a una ubicación Archivos de programa, e iniciar un instalador de controladores. Si un usuario de Windows 7 / Windows Vista (UAC habilitado e incluso si es un administrador local) lo ejecuta sin hacer clic derecho y seleccionar "Ejecutar como administrador", obtendrán 'Acceso Denegado' copiando los dos archivos y escribiendo la variable del sistema.

Me gustaría usar un comando para reiniciar automáticamente el lote como elevado si el usuario es de hecho un administrador. De lo contrario, si no son administradores, quiero decirles que necesitan privilegios de administrador para ejecutar el archivo por lotes. Estoy usando xcopy para copiar los archivos y REG ADD para escribir la variable del sistema. Estoy usando esos comandos para tratar con posibles máquinas Windows XP. He encontrado preguntas similares sobre este tema, pero nada que trate de relanzar un archivo por lotes como elevado.

Author: Matt, 2011-08-12

10 answers

Puede hacer que el script se llame a sí mismo con psexec's -h opción para ejecutar elevado.

No estoy seguro de cómo detectaría si ya se está ejecutando como elevado o no... tal vez volver a intentar con permanentes elevados solo si hay un error de acceso denegado?

O, simplemente podría tener los comandos para xcopy y reg.exe siempre se ejecutan con psexec -h, pero sería molesto para el usuario final si necesita ingresar su contraseña cada vez (o inseguro si incluyó la contraseña en el script)...

 19
Author: ewall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-12 19:04:29

Hay una manera fácil sin la necesidad de usar una herramienta externa - funciona bien con Windows 7, 8, 8.1 y 10 y también es compatible con versiones anteriores (Windows XP no tiene ningún UAC, por lo tanto, no es necesaria la elevación-en ese caso, el script solo procede).

Echa un vistazo a este código (me inspiré en el código de NIronwolf publicado en el hilo Archivo por lotes - "Acceso Denegado" En Windows 7?), pero lo he mejorado - en mi versión no hay ningún directorio creado y eliminado para comprobar los privilegios de administrador):

::::::::::::::::::::::::::::::::::::::::::::
:: Elevate.cmd - Version 4
:: Automatically check & get admin rights
::::::::::::::::::::::::::::::::::::::::::::
 @echo off
 CLS
 ECHO.
 ECHO =============================
 ECHO Running Admin shell
 ECHO =============================

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

El script aprovecha el hecho de que NET FILE requiere privilegios de administrador y devuelve errorlevel 1 si no lo tiene. La elevación se logra creando un script que vuelve a lanzar el archivo por lotes para obtener privilegios. Esto hace que Windows presente el cuadro de diálogo UAC y le solicite la cuenta de administrador y la contraseña.

Lo he probado con Windows 7, 8, 8.1, 10 y con Windows XP-funciona bien para todo. La ventaja es que, después del punto de inicio, puede colocar cualquier cosa que requiera privilegios de administrador del sistema, por ejemplo, si tiene la intención de volver a instalar y ejecutar un servicio de Windows con fines de depuración (suponiendo que mypackage.msi es un paquete de instalación de servicio):

msiexec /passive /x mypackage.msi
msiexec /passive /i mypackage.msi
net start myservice

Sin este script de elevación de privilegios, UAC le preguntaría tres veces por su usuario y contraseña de administrador - ahora solo se le preguntará una vez al principio, y solo si es necesario.


Si su script solo necesita mostrar un mensaje de error y salir si no hay privilegios de administrador en lugar de auto-elevación, esto es aún más simple: Puede lograr esto agregando lo siguiente al principio de su script:

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

De esta manera, el usuario tiene que hacer clic derecho y seleccionar "Ejecutar como administrador". El script continuará después de la instrucción REM si detecta derechos de administrador, de lo contrario saldrá con un error. Si no necesita el PAUSE, simplemente elimine se. Importante: NET FILE [...] EXIT /D) debe estar en la misma línea. Se muestra aquí en varias líneas para una mejor legibilidad!


En algunas máquinas, he encontrado problemas, que ya se resuelven en la nueva versión anterior. Uno se debió a diferentes manejo de comillas dobles, y el otro problema se debió al hecho de que UAC estaba deshabilitado (establecido en el nivel más bajo) en una máquina con Windows 7, por lo que el script se llama a sí mismo una y otra vez.

He arreglado esto ahora quitando las citas en la ruta y volver a añadirlos más tarde, y he añadido un parámetro adicional que se añade cuando el script se vuelve a lanzar con derechos elevados.

Las comillas dobles se eliminan de la siguiente manera (los detalles son aquí):

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

Luego puede acceder a la ruta usando !batchPath!. No contiene comillas dobles, por lo que es seguro decir "!batchPath!" más adelante en el script.

La línea

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

Comprueba si el script ya ha sido llamado por el VBScript script para elevar los derechos, evitando así repeticiones interminables. Elimina el parámetro usando shift.


Actualización:

  • Para evitar tener que registrar la extensión .vbs en Windows 10 , he reemplazado la línea
    "%temp%\OEgetPrivileges.vbs"
    por
    "%SystemRoot%\System32\WScript.exe" "%temp%\OEgetPrivileges.vbs"
    en el script anterior; también se agregó cd /d %~dp0 como lo sugirieron Stephen (respuesta separada) y Tomáš Zato (comentario) para establecer el directorio del script como predeterminado.

  • Ahora el guión honra parámetros de la línea de comandos que se le pasan. Gracias a jxmallet, TanisDLJ y Peter Mortensen por sus observaciones e inspiraciones.

  • De acuerdo con la sugerencia de Artjom B., lo analicé y he reemplazado SHIFT por SHIFT /1, que conserva el nombre del archivo para el parámetro %0

  • Se agregó del "%temp%\OEgetPrivileges_%batchName%.vbs" a la sección :gotPrivileges para limpiar (como se sugirió mlt). Se agregó %batchName% para evitar el impacto si ejecuta diferentes lotes en paralelo. Tenga en cuenta que debe usar for para ser capaz de aprovechar las funciones de cadena avanzadas, como %%~nk, que extrae solo el nombre del archivo.

  • Estructura de script optimizada, mejoras (se agregó la variable vbsGetPrivileges que ahora se hace referencia en todas partes, lo que permite cambiar la ruta o el nombre del archivo fácilmente, solo eliminar el archivo .vbs si el lote necesita ser elevado)

  • En algunos casos, se requería una sintaxis de llamada diferente para la elevación. Si el script no funciona, compruebe lo siguiente parámetros:
    set cmdInvoke=0
    set winSysFolder=System32
    Cambie el parámetro 1st a set cmdInvoke=1 y compruebe si eso ya soluciona el problema. Añadirá cmd.exe al script que realiza la elevación.
    O intente cambiar el segundo parámetro a winSysFolder=Sysnative, esto podría ayudar (pero en la mayoría de los casos no es necesario) en sistemas de 64 bits. (ADBailey ha informado de esto). "Sysnative" solo se requiere para iniciar aplicaciones de 64 bits desde un host de script de 32 bits (por ejemplo, un proceso de compilación de Visual Studio o invocación de script desde otra aplicación de 32 bits).

  • Para que quede más claro cómo se interpretan los parámetros, lo estoy mostrando ahora como P1=value1 P2=value2 ... P9=value9. Esto es especialmente útil si necesita incluir parámetros como rutas entre comillas dobles, por ejemplo "C:\Program Files".

 295
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-20 07:14:10

Como jcoder y Matt mencionaron, PowerShell lo hizo fácil, e incluso se podía incrustar en el script por lotes sin crear un nuevo script.

Modifiqué el guión de Matt:

:checkPrivileges 
NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' ( goto gotPrivileges 
) else ( powershell "saps -filepath %0 -verb runas" >nul 2>&1)
exit /b 

No hay ninguna necesidad de la etiqueta :getPrivileges.

 29
Author: Ir Relevant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-29 23:47:12

Estoy usando la excelente respuesta de Matt, pero estoy viendo una diferencia entre mis sistemas Windows 7 y Windows 8 al ejecutar scripts elevados.

Una vez que el script está elevado en Windows 8, el directorio actual se establece en C:\Windows\system32. Afortunadamente, hay una solución fácil cambiando el directorio actual a la ruta del script actual:

cd /d %~dp0

Nota: Use cd /d para asegurarse de que la letra de la unidad también se cambie.

Para probar esto, puede copiar lo siguiente a un script. Ejecute normalmente en cualquiera de las versiones para ver el mismo resultado. Ejecutar como administrador y ver la diferencia en 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
 27
Author: Stephen Klancher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-08 12:07:28

Lo hago de esta manera:

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

De esta manera es simple y solo usa comandos predeterminados de Windows. Es genial si necesitas redistribuir tu archivo por lotes.

CD /d %~dp0 Establece el directorio actual en el directorio actual del archivo (si aún no lo está, independientemente de la unidad en la que se encuentre el archivo, gracias a la opción /d).

%~nx0 Devuelve el nombre de archivo actual con extensión (Si no incluye la extensión y hay un exe con el mismo nombre en la carpeta, llamará al exe).

Hay tantas respuestas en este post que ni siquiera sé si mi respuesta será vista.

De todos modos, encuentro esta manera más simple que las otras soluciones propuestas en las otras respuestas, espero que ayude a alguien.

 24
Author: Matheus Rocha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-13 13:50:25

Matt tiene una gran respuesta, pero elimina cualquier argumento pasado al script. Aquí está mi modificación que mantiene los argumentos. También incorporé la solución de Stephen para el problema del directorio de trabajo en Windows 8.

@ECHO OFF
setlocal EnableDelayedExpansion

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...
 19
Author: jxmallett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-07 00:57:57

Utilizo PowerShell para volver a lanzar el script elevado si no lo es. Ponga estas líneas en la parte superior de su guión.

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

Copié el método 'net name' de la respuesta de @Matt. Su respuesta está mucho mejor documentada y tiene mensajes de error y similares. Este tiene la ventaja de que PowerShell ya está instalado y disponible en Windows 7 y superiores. No hay VBScript temporal ( * .vbs), y no tiene que descargar herramientas.

Este método debería funcionar sin ninguna configuración o configuración, siempre y cuando los permisos de ejecución de PowerShell no estén bloqueados.

 16
Author: Ryan Bemrose,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-29 23:52:05

Para algunos programas establecer la variable de entorno super secret __COMPAT_LAYER a RunAsInvoker funcionará .Compruebe esto:

set "__COMPAT_LAYER=RunAsInvoker"
start regedit.exe

Aunque así no habrá UAC solicitando que el usuario continúe sin permisos de administrador.

 12
Author: npocmaka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-31 09:11:04

Pegué esto al principio del script:

:: 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"
:--------------------------------------
 5
Author: TanisDLJ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-23 16:51:51

La siguiente solución está limpia y funciona perfectamente.

  1. Descargar archivo zip Elevate desde https://www.winability.com/download/Elevate.zip

  2. Dentro de zip debes encontrar dos archivos: Elevate.exe y Elevado64.exe. (Este último es una compilación nativa de 64 bits, si necesita que, aunque la versión regular de 32 bits, Eleve.exe, debería funcionar bien con las versiones de 32 y 64 bits de Windows)

  3. Copia el archivo Elevate.exe en una carpeta donde Windows siempre puede encontrarlo (como C:/Windows). O mejor que usted puede copiar en la misma carpeta donde usted está planeando mantener su archivo bat.

  4. Para usarlo en un archivo por lotes, simplemente anteponga el comando que desea ejecutar como administrador con el comando elevate, así:

 elevate net start service ...
 0
Author: Vaibhav Jain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-24 17:29:12