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

Resuelto PDixon724 asked hace 13 años • 19 respuestas

Quiero que mi archivo por lotes solo se ejecute elevado. Si no está elevado, proporcione una opción para que el usuario reinicie el lote como elevado.

Estoy escribiendo un archivo por lotes para configurar una variable del sistema, copiar dos archivos a una ubicación de 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á "Acceso denegado" al copiar los dos archivos y escribir la variable del sistema. .

Me gustaría usar un comando para reiniciar automáticamente el lote como elevado si el usuario es en realidad administrador. De lo contrario, si no es administrador, 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 con Windows XP. Encontré preguntas similares sobre este tema, pero nada que tenga que ver con reiniciar un archivo por lotes como elevado.

PDixon724 avatar Aug 13 '11 01:08 PDixon724
Aceptado

Existe una manera fácil sin la necesidad de utilizar una herramienta externa: funciona bien con Windows 7, 8, 8.1, 10 y 11 y también es compatible con versiones anteriores (Windows XP no tiene UAC, por lo que no es necesaria la elevación). en ese caso el guión simplemente continúa).

Consulte este código (me inspiré en el código de NIronwolf publicado en el hilo Archivo por lotes - "Acceso denegado" en Windows 7?  1) , pero lo he mejorado: en mi versión no se crea ni se elimina ningún directorio. para comprobar los privilegios de administrador):

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

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

Lo probé con Windows 7, 8, 8.1, 10, 11 y con Windows XP; funciona bien para todos. La ventaja es que, después del punto de inicio, puede colocar cualquier cosa que requiera privilegios de administrador del sistema, por ejemplo, si desea 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 servicios). :

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

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


Si su secuencia de comandos solo necesita mostrar un mensaje de error y salir si no hay privilegios de administrador en lugar de elevarse automáticamente, esto es aún más simple: puede lograrlo agregando lo siguiente al comienzo de su secuencia de comandos:

@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 forma, el usuario debe hacer clic derecho y seleccionar "Ejecutar como administrador" . El script continuará después de la REMdeclaración si detecta derechos de administrador; de lo contrario, saldrá con un error. Si no lo necesita PAUSE, simplemente elimínelo. 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 están resueltos en la nueva versión anterior. Uno se debió a un manejo diferente de las 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.

Solucioné esto ahora eliminando las comillas en la ruta y volviéndolas a agregar más tarde, y agregué un parámetro adicional que se agrega cuando el script se reinicia con derechos elevados.

Las comillas dobles se eliminan con lo siguiente (los detalles están 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 decirlo "!batchPath!"más adelante en el guión.

La línea

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

comprueba si el script VBScript ya ha llamado al script para elevar los derechos, evitando así recursiones interminables. Elimina el parámetro usando shift.


Actualizar:

  • Para evitar tener que registrar la .vbsextensión en Windows 10 , reemplacé la línea
    "%temp%\OEgetPrivileges.vbs"
    por
    "%SystemRoot%\System32\WScript.exe" "%temp%\OEgetPrivileges.vbs"
    en el script anterior; También se agregó cd /d %~dp0según lo sugerido por Stephen (respuesta separada) y Tomáš Zato (comentario) para configurar el directorio de secuencias de comandos como predeterminado.

  • Ahora el script respeta los parámetros de la línea de comando 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 lo reemplacé SHIFTpor SHIFT /1, que conserva el nombre del archivo para el %0parámetro.

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

  • Estructura de script optimizada, mejoras (variable agregada vbsGetPrivilegesa la que ahora se hace referencia en todas partes, lo que permite cambiar la ruta o el nombre del archivo fácilmente, solo elimine .vbsel archivo si es necesario elevar el lote)

  • En algunos casos, se requirió una sintaxis de llamada diferente para la elevación. Si el script no funciona, verifique los siguientes parámetros:
    set cmdInvoke=0
    set winSysFolder=System32
    Cambie el primer parámetro a set cmdInvoke=1y verifique si eso ya soluciona el problema. Se agregará cmd.exeal script realizando 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 esto). "Sysnative" sólo es necesario 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 la invocación de un script desde otra aplicación de 32 bits).

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

  • Si desea depurar el script VBS, puede agregar el //Xparámetro a WScript.exe como primer parámetro, como se sugiere aquí (se describe para CScript.exe, pero también funciona para WScript.exe).

  • Corrección de error proporcionada por MiguelAngelo : BatchPath ahora se devuelve correctamente en cmd Shell. Este pequeño script test.cmdmuestra la diferencia, para aquellos interesados ​​en los detalles (ejecútelo en cmd.exe, luego ejecútelo haciendo doble clic desde el Explorador de Windows):

    @echo off
    setlocal
    set a="%~0"
    set b="%~dpnx0"
    if %a% EQU %b% echo running shell execute
    if not %a% EQU %b% echo running cmd shell
    echo a=%a%, b=%b% 
    pause
    

Enlaces útiles:

  • Significado de los caracteres especiales en el archivo por lotes:
    Comillas (") , Bang (!) , Caret (^) , Ampersand (&) , Otros caracteres especiales

1) Tenga en cuenta que este enlace ya no existe; ahora muestra un error 404.

Matt avatar Sep 04 '2012 13:09 Matt

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

Modifiqué el guión de Matt:

:: 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
Ir Relevant avatar Jul 09 '2014 23:07 Ir Relevant

Yo 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 usa solo los comandos predeterminados de Windows. Es genial si necesita redistribuir su archivo por lotes.

CD /d %~dp0Establece 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 /dopción).

%~nx0Devuelve el nombre del archivo actual con la 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 esta publicación que ni siquiera sé si se verá mi respuesta.

De todos modos, esta forma me parece más sencilla que las otras soluciones propuestas en las otras respuestas, espero que ayude a alguien.

Matheus Rocha avatar Jun 07 '2016 02:06 Matheus Rocha