Cómo solicitar acceso de administrador dentro de un archivo por lotes


179

Estoy tratando de escribir un archivo por lotes para que mis usuarios lo ejecuten desde sus máquinas Vista con UAC. El archivo está reescribiendo su archivo de hosts, por lo que debe ejecutarse con permisos de administrador. Necesito poder enviarles un correo electrónico con un enlace al archivo .bat. El comportamiento deseado es que cuando hacen clic con el botón derecho en el archivo y dicen Abrir, obtendrán uno de esos cuadros de diálogo de UAC que oscurecen la pantalla y los obligan a responder si desean dar permiso a la aplicación para ejecutarse como administrador. En cambio, solo están viendo "Acceso denegado" en la ventana de la línea de comandos.

¿Es esto posible hacer de manera diferente?


2
Si te encuentras con esto y, como yo, estás contento con el uso de PowerShell, no te pierdas el one-liner de @ toster-cx. ¡Perfecto!
Michael Repucci

Respuestas:


352

Este script hace el truco! Solo pégalo en la parte superior de tu archivo bat. Si desea revisar el resultado de su secuencia de comandos, agregue un comando de "pausa" en la parte inferior de su archivo por lotes.

ACTUALIZACIÓN: Este script ahora está ligeramente editado para admitir argumentos de línea de comando y un sistema operativo de 64 bits.

Gracias Eneerge @ https://sites.google.com/site/eneerge/scripts/batchgotadmin

@echo off

:: BatchGotAdmin
:-------------------------------------
REM  --> Check for permissions
    IF "%PROCESSOR_ARCHITECTURE%" EQU "amd64" (
>nul 2>&1 "%SYSTEMROOT%\SysWOW64\cacls.exe" "%SYSTEMROOT%\SysWOW64\config\system"
) ELSE (
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.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"
    set params= %*
    echo UAC.ShellExecute "cmd.exe", "/c ""%~s0"" %params:"=""%", "", "runas", 1 >> "%temp%\getadmin.vbs"

    "%temp%\getadmin.vbs"
    del "%temp%\getadmin.vbs"
    exit /B

:gotAdmin
    pushd "%CD%"
    CD /D "%~dp0"
:--------------------------------------    
    <YOUR BATCH SCRIPT HERE>

22
Odio tener que hacer estas tonterías sucias de dos lotes, pero a veces te ves obligado a hacerlo y esto funciona muy bien. ¡Salud!
Matt quema el

44
Este método no reenvía argumentos. ¿Sabes cómo se puede hacer eso? Básicamente, lo que observo es que en la primera línea% 1 tiene algún valor y en la última línea% 1 es nulo. Necesito reenviar los argumentos.
dientes

3
Al igual que para su información, esto se prueba como trabajando en Windows 8 Embedded
Robert Snyder

66
Esto arroja mi máquina en una espiral de ventanas de comandos que se abren y cierran en diagonal a través de la pantalla. La única forma de detenerlo es eliminar el archivo por lotes original. Ejecuta repetidamente mi archivo por lotes y escribe el archivo vbs. La primera vez solicitó autorización, pero después de eso solo se repite.
TomDestry

2
Me encontré exactamente con el mismo problema que TomDestry con el bucle infinito y el código de retorno 2. Esto estaba en Windows 8.1. Sé que funcionó en Windows 8.0, y no puedo decir con certeza si fue la actualización 8.1 o algo más que causó la emisión. La solución que funcionó para mí fue no usar cacls.exe (o icacls), sino: net session> nul 2> & 1 IF ERRORLEVEL 1 goto UACPrompt ...
crig

55

Aquí hay una frase que he estado usando:

@echo off
if not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit /b)

echo main code here
pause

Notas:

  • Solo probado en Windows 7 y 10, es posible que tenga que perder el tiempo con la cita
  • No soporta pasar argumentos por ahora

1
Si sabe cuántos parámetros hay, puede pasar los parámetros incluyéndolos después de que am_admin if not "%1"=="am_admin" (powershell start -verb runas '%0' 'am_admin "%~1" "%~2"' & exit)los parámetros estén uno arriba de donde estaban
Tony Brix

También es posible usarlo 'am_admin %*'para pasar todo, no funciona bien con comillas y espacios aunque: / Puedes usarlo shiften lote para reventar el primer argumento, arreglando así todos los argumentos excepto %0.
toster-cx

Esta es una buena respuesta, pero no debe aceptarse, ya que NO comprueba si en la PRIMERA ejecución se ejecutó con privilegio ADMIN o no.
T.Todua

Para mantener el directorio de trabajo, agregue cd /D %~dp0despuésif not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit /b)
Pedro Duarte

Buena respuesta. Simplemente funciona mientras la secuencia de comandos VB crea un bucle infinito, incluso con permiso de escritura para% temp%.
Twonky

19

Aquí está mi código! Parece grande, pero se trata principalmente de líneas de comentarios (las líneas que comienzan con: :).

caracteristicas:

  • Reenvío completo de argumentos
  • No cambia la carpeta de trabajo
  • Manejo de errores
  • Acepta rutas con paréntesis (excepto la carpeta% TEMP%)
  • Admite rutas UNC
  • Verificación de carpeta asignada (le advierte si el administrador no puede acceder a la unidad asignada)

  • Se puede utilizar como una biblioteca externa (consulte mi publicación en este tema: https://stackoverflow.com/a/30417025/4932683 )

  • Se puede llamar cuando / si es necesario en cualquier parte de su código

Simplemente adjunte esto al final de su archivo por lotes, o guárdelo como una biblioteca (verifique arriba)

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:RequestAdminElevation FilePath %* || goto:eof
:: 
:: By:   Cyberponk,     v1.5 - 10/06/2016 - Changed the admin rights test method from cacls to fltmc
::          v1.4 - 17/05/2016 - Added instructions for arguments with ! char
::          v1.3 - 01/08/2015 - Fixed not returning to original folder after elevation successful
::          v1.2 - 30/07/2015 - Added error message when running from mapped drive
::          v1.1 - 01/06/2015
:: 
:: Func: opens an admin elevation prompt. If elevated, runs everything after the function call, with elevated rights.
:: Returns: -1 if elevation was requested
::           0 if elevation was successful
::           1 if an error occured
:: 
:: USAGE:
:: If function is copied to a batch file:
::     call :RequestAdminElevation "%~dpf0" %* || goto:eof
::
:: If called as an external library (from a separate batch file):
::     set "_DeleteOnExit=0" on Options
::     (call :RequestAdminElevation "%~dpf0" %* || goto:eof) && CD /D %CD%
::
:: If called from inside another CALL, you must set "_ThisFile=%~dpf0" at the beginning of the file
::     call :RequestAdminElevation "%_ThisFile%" %* || goto:eof
::
:: If you need to use the ! char in the arguments, the calling must be done like this, and afterwards you must use %args% to get the correct arguments:
::      set "args=%* "
::      call :RequestAdminElevation .....   use one of the above but replace the %* with %args:!={a)%
::      set "args=%args:{a)=!%" 
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
setlocal ENABLEDELAYEDEXPANSION & set "_FilePath=%~1"
  if NOT EXIST "!_FilePath!" (echo/Read RequestAdminElevation usage information)
  :: UAC.ShellExecute only works with 8.3 filename, so use %~s1
  set "_FN=_%~ns1" & echo/%TEMP%| findstr /C:"(" >nul && (echo/ERROR: %%TEMP%% path can not contain parenthesis &pause &endlocal &fc;: 2>nul & goto:eof)
  :: Remove parenthesis from the temp filename
  set _FN=%_FN:(=%
  set _vbspath="%temp:~%\%_FN:)=%.vbs" & set "_batpath=%temp:~%\%_FN:)=%.bat"

  :: Test if we gave admin rights
  fltmc >nul 2>&1 || goto :_getElevation

  :: Elevation successful
  (if exist %_vbspath% ( del %_vbspath% )) & (if exist %_batpath% ( del %_batpath% )) 
  :: Set ERRORLEVEL 0, set original folder and exit
  endlocal & CD /D "%~dp1" & ver >nul & goto:eof

  :_getElevation
  echo/Requesting elevation...
  :: Try to create %_vbspath% file. If failed, exit with ERRORLEVEL 1
  echo/Set UAC = CreateObject^("Shell.Application"^) > %_vbspath% || (echo/&echo/Unable to create %_vbspath% & endlocal &md; 2>nul &goto:eof) 
  echo/UAC.ShellExecute "%_batpath%", "", "", "runas", 1 >> %_vbspath% & echo/wscript.Quit(1)>> %_vbspath%
  :: Try to create %_batpath% file. If failed, exit with ERRORLEVEL 1
  echo/@%* > "%_batpath%" || (echo/&echo/Unable to create %_batpath% & endlocal &md; 2>nul &goto:eof)
  echo/@if %%errorlevel%%==9009 (echo/^&echo/Admin user could not read the batch file. If running from a mapped drive or UNC path, check if Admin user can read it.)^&echo/^& @if %%errorlevel%% NEQ 0 pause >> "%_batpath%"

  :: Run %_vbspath%, that calls %_batpath%, that calls the original file
  %_vbspath% && (echo/&echo/Failed to run VBscript %_vbspath% &endlocal &md; 2>nul & goto:eof)

  :: Vbscript has been run, exit with ERRORLEVEL -1
  echo/&echo/Elevation was requested on a new CMD window &endlocal &fc;: 2>nul & goto:eof
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Ejemplo sobre cómo usarlo

:EXAMPLE
@echo off

 :: Run this script with elevation
 call :RequestAdminElevation "%~dpfs0" %* || goto:eof

  echo/I now have Admin rights!
  echo/
  echo/Arguments using %%args%%:    %args%
  echo/Arguments using %%*: %*
  echo/%%1= %~1
  echo/%%2= %~2
  echo/%%3= %~3

  echo/
  echo/Current Directory: %CD%
  echo/
  echo/This file: %0
  echo/

pause &goto:eof

[here you paste the RequestAdminElevation function code]

Funciona muy bien, pero tuve que cambiar una línea para que funcione. El &fc;: 2>nulen set "_FN=_%~ns1" & echo/%TEMP%| findstr /C:"(" >nul && (echo/ERROR: %%TEMP%% path can not contain parenthesis &pause &endlocal &fc;: 2>nul & goto:eof)fijaba el nivel de error a 1. He quitado ese poco y funcionó perfecto. Estoy usando Windows 10 Home.
Knyri

¿Tu carpeta% temp% tiene paréntesis en su ruta? Solo en este caso se supone que se debe establecer el nivel de error 1.
cyberponk

No ¿Necesita el fc;:ya que va: eof justo después? Tampoco estoy seguro de por qué ese pequeño problema causó el problema, ya que debería ejecutarse.
Knyri

"fc ;: 2> nul" está allí intencionalmente para configurar ERRORLEVEL 1 antes de salir, para señalar un error. ¿Podría eliminar el @echo y ejecutarlo y enviarme la salida en un mensaje privado? ¡Gracias!
cyberponk

De todos modos, echo/%TEMP%| findstr /C:"(" >nulcomprueba si hay una variable de entorno (char en su %temp%entorno, y solo debe ejecutar la parte después de &&si es positiva. Sin (embargo, es extraño por qué su prueba es positiva.
cyberponk

7

Otro enfoque es

  • crear un acceso directo localmente y configurarlo para que solicite permiso de administrador [Propiedades, Avanzado, Ejecutar como administrador]

y entonces

  • envíe a sus usuarios el acceso directo [o un enlace al acceso directo en lugar de uno al archivo por lotes en sí].

Denis

[Agregado después - Sí, no noté la fecha de este hilo.]


6

La solución de Ben Gripka provoca bucles infinitos. Su lote funciona así (pseudocódigo):

IF "no admin privileges?"
    "write a VBS that calls this batch with admin privileges"
ELSE
    "execute actual commands that require admin privileges"

Como puede ver, esto provoca un bucle infinito, si el VBS falla al solicitar privilegios de administrador.

Sin embargo, el ciclo infinito puede ocurrir, aunque los privilegios de administrador se han solicitado con éxito.

La verificación en el archivo por lotes de Ben Gripka es propensa a errores. Jugué con el lote y observé que los privilegios de administrador están disponibles aunque la verificación falló. Curiosamente, la verificación funcionó como se esperaba, si comencé el archivo por lotes desde el explorador de Windows, pero no lo hizo cuando lo inicié desde mi IDE.

Así que sugiero usar dos archivos por lotes separados. El primero genera el VBS que llama al segundo archivo por lotes:

@echo off

echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
set params = %*:"=""
echo UAC.ShellExecute "cmd.exe", "/c ""%~dp0\my_commands.bat"" %params%", "", "runas", 1 >> "%temp%\getadmin.vbs"

"%temp%\getadmin.vbs"
del "%temp%\getadmin.vbs"

El segundo, llamado "my_commands.bat" y ubicado en el mismo directorio que el primero contiene sus comandos reales:

pushd "%CD%"
CD /D "%~dp0"
REM Your commands which require admin privileges here

Esto no provoca bucles infinitos y también elimina la comprobación de privilegios de administrador propensa a errores.


Trabajado para ti? Lamentablemente para mí, este y todos los demás aquí y del otro hilo fallan debido al mismo problema subyacente. El "runas" parm (o comando) falla cada vez que el Objeto Shell se crea indirectamente desde otro programa. Hilo de interés aquí .
Laurie Stearn

Trabajó para mí :)
Sritam Jagadev

6

Otra solución de PowerShell ...

No se trata de ejecutar un script por lotes como administrador, sino de cómo elevar otro programa del lote ...

Tengo un archivo por lotes "contenedor" para un exe. Tienen el mismo "nombre de archivo raíz", pero extensiones alternativas. Puedo iniciar el exe como administrador y establecer el directorio de trabajo en el que contiene el script, con la siguiente invocación de PowerShell de una línea :

@powershell "Start-Process -FilePath '%~n0.exe' -WorkingDirectory '%~dp0' -Verb RunAs"

Más información

¡También hay una gran cantidad de Start-Processopciones adicionales que puedes aplicar! Echa un vistazo: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-6

Tenga en cuenta que uso el @prefijo. Eso es equivalente a @echo offpara la una línea. Utilizo %~n0aquí para obtener el "nombre raíz" del script por lotes, luego concateno el .exepara señalarlo como el binario adjunto. El uso de %~dp0proporciona la ruta completa al directorio donde reside el lote. Y, por supuesto, el -Verb RunAsparámetro proporciona la elevación .


4

Sé que esta no es una solución para OP, pero como estoy seguro de que hay muchos otros casos de uso aquí, pensé en compartir.

He tenido problemas con todos los ejemplos de código en estas respuestas, pero luego encontré: http://www.robotronic.de/runasspcEn.html

No solo le permite ejecutarse como administrador, sino que comprueba el archivo para asegurarse de que no haya sido manipulado y almacena la información necesaria de forma segura. Admito que no es la herramienta más obvia para descubrir cómo usarla, pero para aquellos de nosotros que escribimos código, debería ser lo suficientemente simple.


3

@echo offy titlepuede venir antes de este código:

net session>nul 2>&1
if %errorlevel%==0 goto main
echo CreateObject("Shell.Application").ShellExecute "%~f0", "", "", "runas">"%temp%/elevate.vbs"
"%temp%/elevate.vbs"
del "%temp%/elevate.vbs"
exit

:main
    <code goes here>
exit

Muchas de las otras respuestas son excesivas si no necesita preocuparse por lo siguiente:

  • Parámetros
  • Directorio de trabajo ( cd %~dp0cambiará al directorio que contiene el archivo por lotes)

3
@echo off 
Net session >nul 2>&1 || (PowerShell start -verb runas '%~0' &exit /b)
Echo Administrative privileges have been got. & pause

Lo anterior funciona en mi Windows 10 versión 1903


1

Dado que tengo problemas con este script, aparece un nuevo símbolo del sistema que se ejecuta nuevamente, en bucle infinito (usando Win 7 Pro), le sugiero que pruebe otro enfoque: ¿Cómo puedo elevar automáticamente mi archivo por lotes, para que solicite a ¿Derechos de administrador de UAC si es necesario?

Tenga cuidado, debe agregar esto al final del script, como se indica en una edición, para que pueda volver al directorio del script después de que se hayan elevado los privilegios: cd / d% ~ dp0


Mira mi respuesta a esta pregunta. Maneja todos estos problemas.
cyberponk

1

Según la publicación de toster-cx y otras publicaciones interesantes en esta página, obtuve información sobre cómo configurar y resolver mi problema. Tuve un problema similar en el que deseaba que la utilidad Disk Cleanup se ejecute cada semana dos veces los lunes y jueves durante las horas de almuerzo (por ejemplo, 2 pm). Sin embargo, esto requería derechos elevados.

Compartir archivos por lotes que podrían ayudar a otros principiantes como yo:

@echo off
echo  Welcome to scheduling 'PC Maintenance Activity'
ping localhost -n 3 >nul
echo -- Step - 1 of 3 : Please give 'Admin' rights on next screen
ping localhost -n 5 >nul
if not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit)
cls
echo -- Step - 2 of 3 : In next screen, select temp areas for cleaning 
during routine scheduled activity
ping localhost -n 3 >nul
C:\Windows\System32\cleanmgr.exe /sageset:112
cls
echo    Now scheduling maintenance activity...
SchTasks /Create /SC WEEKLY /D MON,THU /TN PC_Cleanup /TR 
"C:\Windows\System32\cleanmgr.exe "/sagerun:112 /ST 14:00

cls

echo                         -- Thanks for your co-operation --
echo                    -- Maintenance activity is scheduled for --
echo                       -- Every Monday and Thursday at 2 pm --

ping localhost -n 10 >nul

Muchas gracias por este foro y Rems POST aquí [ https://www.petri.com/forums/forum/windows-scripting/general-scripting/32313-schtasks-exe-need-to-pass-parameters-to-script ] [1]

Su publicación ayudó a configurar el argumento opcional al programar la tarea.


1

También está la consulta FSUTIL de esta publicación, que también está vinculada en ss64.com que tiene el siguiente código:

@Echo Off
Setlocal
:: First check if we are running As Admin/Elevated
FSUTIL dirty query %SystemDrive% >nul
if %errorlevel% EQU 0 goto START

::Create and run a temporary VBScript to elevate this batch file
   Set _batchFile=%~f0
   Set _Args=%*
   :: double up any quotes
   Set _batchFile=""%_batchFile:"=%""
   Set _Args=%_Args:"=""%

   Echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\~ElevateMe.vbs"
   Echo UAC.ShellExecute "cmd", "/c ""%_batchFile% %_Args%""", "", "runas", 1 >> "%temp%\~ElevateMe.vbs"

   cscript "%temp%\~ElevateMe.vbs" 
   Exit /B

:START
:: set the current directory to the batch file location
cd /d %~dp0
:: Place the code which requires Admin/elevation below
Echo We are now running as admin [%1] [%2]
pause

Mientras FSUTIL esté cerca, es una alternativa confiable.


0

No puede solicitar derechos de administrador desde un archivo por lotes, pero puede escribir un script de host de scripts de Windows en% temp% y ejecutarlo (y eso a su vez ejecuta su lote como administrador). Desea llamar al método ShellExecute en el Shell. Objeto de aplicación con "runas" como verbo


0

Use mshtapara solicitar derechos de administrador:

@echo off
net session >nul 2>&1 && goto :admintasks
MSHTA "javascript: var shell = new ActiveXObject('shell.application'); shell.ShellExecute('%~nx0', '', '', 'runas', 1);close();"
exit /b
:admintasks
rem ADMIN TASKS HERE

O, usando powershell:

powershell -c Start-Process "%~nx0" -Verb runas

-5

usa el comando runas. Pero, no creo que pueda enviar un archivo .bat fácilmente.


66
Esta respuesta es incorrecta. El runascomando no puede usarse para provocar elevación.
Bill_Stewart
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.