web-dev-qa-db-fra.com

Comment puis-je élever automatiquement mon fichier de commandes, afin qu'il demande les droits d'administrateur de l'UAC si nécessaire?

Je veux que mon fichier de commandes ne fonctionne que dans des conditions élevées. S'il n'est pas élevé, fournissez une option à l'utilisateur pour relancer le lot en tant que élevé.

J'écris un fichier de commandes pour définir une variable système, copier deux fichiers dans un emplacement Program Files et lancer un programme d'installation du pilote. Si un utilisateur Windows 7/Windows Vista ( UAC activé et même s'il s'agit d'un administrateur local) l'exécute sans cliquer avec le bouton droit de la souris et en sélectionnant "Exécuter en tant qu'administrateur", il obtiendra le message "Accès refusé". copier les deux fichiers et écrire la variable système.

Je voudrais utiliser une commande pour redémarrer automatiquement le lot comme élevé si l'utilisateur est en fait un administrateur. Sinon, s'ils ne sont pas administrateurs, je veux leur dire qu'ils ont besoin de privilèges d'administrateur pour exécuter le fichier de traitement par lots. J'utilise xcopy pour copier les fichiers et REG ADD pour écrire la variable système. J'utilise ces commandes pour gérer d'éventuelles machines Windows XP. J'ai trouvé des questions similaires sur ce sujet, mais rien ne traite de la relance d'un fichier de commandes avec un niveau élevé.

194
PDixon724

Vous pouvez demander au script de s’appeler lui-même avec l’option -h de psexec pour exécuter une exécution élevée.

Je ne sais pas comment vous allez détecter s'il fonctionne déjà en mode élevé ou non ... réessayez avec des permanences élevées uniquement en cas d'erreur d'accès refusé.

Ou, vous pourriez simplement avoir les commandes pour xcopy et reg.exe toujours être exécutées avec psexec -h, mais cela serait ennuyeux pour l'utilisateur final s'il doit entrer son mot de passe à chaque fois. (ou non sécurisé si vous avez inclus le mot de passe dans le script) ...

20
ewall

Il existe un moyen simple de ne pas utiliser d’outil externe: il fonctionne bien avec Windows 7, 8, 8.1 et 10 et est rétro-compatible aussi. (Windows XP n’ayant pas de UAC, l’élévation n’est pas nécessaire - dans ce cas, le script se poursuit).

Découvrez ce code (j'ai été inspiré par le code de NIronwolf publié dans le fil de discussion Fichier de commandes - "Accès refusé" sous Windows 7? ), mais Je l'ai amélioré - dans ma version, aucun répertoire n'a été créé ni supprimé pour vérifier les privilèges d'administrateur):

_::::::::::::::::::::::::::::::::::::::::::::
:: 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=%~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
_

Le script tire parti du fait que NET FILE requiert le privilège d’administrateur et renvoie _errorlevel 1_ si vous ne l’avez pas. L'élévation est obtenue en créant un script qui relance le fichier de commandes pour obtenir des privilèges. Ainsi, Windows présente la boîte de dialogue UAC et vous demande le compte d’administrateur et le mot de passe.

Je l'ai testé avec Windows 7, 8, 8.1, 10 et Windows XP - tout fonctionne bien. L'avantage est qu'après le point de départ, vous pouvez placer tout ce qui nécessite des privilèges d'administrateur système, par exemple, si vous avez l'intention de réinstaller et de réexécuter un service Windows à des fins de débogage (en supposant que mypackage.msi est un package d'installation de service) :

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

Sans ce script d'élévation de privilèges, UAC vous demanderait trois fois votre nom d'utilisateur et votre mot de passe administrateur. Désormais, il ne vous sera demandé qu'une fois au début, et uniquement si nécessaire.


Si votre script doit simplement afficher un message d'erreur et quitter s'il n'y a pas de privilèges d'administrateur au lieu de élévation automatique, la procédure est encore plus simple: vous pouvez y parvenir en ajoutant ce qui suit au début de votre 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 cette façon, l'utilisateur doit cliquer avec le bouton droit de la souris et sélectionner "Exécuter en tant qu'administrateur" . Le script se poursuivra après l'instruction REM s'il détecte les droits d'administrateur, sinon quitte avec une erreur. Si vous n'avez pas besoin de PAUSE, supprimez-le simplement. Important: _NET FILE [...] EXIT /D)_ doit être sur la même ligne. Il est affiché ici sur plusieurs lignes pour une meilleure lisibilité!


Sur certaines machines, J'ai déjà rencontré des problèmes qui sont déjà résolus dans la nouvelle version. L'une était due à une gestion différente des guillemets, l'autre problème était dû au fait que le contrôle de compte d'utilisateur était désactivé (défini au niveau le plus bas) sur un ordinateur Windows 7, de sorte que le script s'appelle encore et encore.

J'ai résolu ce problème en supprimant les guillemets dans le chemin et en les rajoutant plus tard, et j'ai ajouté un paramètre supplémentaire qui est ajouté lorsque le script est relancé avec des droits élevés.

Les guillemets sont supprimés comme suit (Les détails sont ici ):

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

Vous pouvez ensuite accéder au chemin en utilisant _!batchPath!_. Il ne contient pas de guillemets, il est donc prudent de dire _"!batchPath!"_ plus tard dans le script.

La ligne

_if '%1'=='ELEV' (shift & goto gotPrivileges)
_

vérifie si le script a déjà été appelé par le script VBScript pour élever les droits, évitant ainsi des récursions sans fin. Il supprime le paramètre en utilisant shift.


Mise à jour:

  • Pour éviter d'avoir à enregistrer l'extension _.vbs_ dans Windows 10 , j'ai remplacé la ligne
    _"%temp%\OEgetPrivileges.vbs"_
    par
    _"%SystemRoot%\System32\WScript.exe" "%temp%\OEgetPrivileges.vbs"_
    dans le script ci-dessus; également ajouté _cd /d %~dp0_ comme suggéré par Stephen (réponse séparée) et par Tomáš Zato (commentaire) pour définir le répertoire de script par défaut.

  • Maintenant, le script respecte les paramètres de ligne de commande qui lui sont transmis. Merci à jxmallet, TanisDLJ et Peter Mortensen pour leurs observations et inspirations.

  • Selon les indications d'Artjom B., je l'ai analysé et j'ai remplacé SHIFT par _SHIFT /1_, qui conserve le nom du fichier pour le paramètre _%0_.

  • Ajout de _del "%temp%\OEgetPrivileges_%batchName%.vbs"_ à la section _:gotPrivileges_ à nettoyer (comme mlt suggéré). Ajout de _%batchName%_ pour éviter tout impact si vous exécutez différents lots en parallèle. Notez que vous devez utiliser for pour pouvoir tirer parti des fonctions de chaîne avancées, telles que _%%~nk_, qui extrait uniquement le nom du fichier.

  • Structure de script optimisée, améliorations (variable ajoutée vbsGetPrivileges désormais référencée partout permettant de modifier facilement le chemin ou le nom du fichier, supprime uniquement le fichier _.vbs_ si le traitement par lots doit être élevé)

  • Dans certains cas, une syntaxe d'appel différente était requise pour l'élévation. Si le script ne fonctionne pas, vérifiez les paramètres suivants:
    _set cmdInvoke=0_
    _set winSysFolder=System32_
    Modifiez le premier paramètre en _set cmdInvoke=1_ et vérifiez si le problème est déjà résolu. Il ajoutera _cmd.exe_ au script effectuant l’élévation.
    Ou essayez de changer le deuxième paramètre en _winSysFolder=Sysnative_, cela pourrait aider (mais dans la plupart des cas, ce n'est pas obligatoire) sur les systèmes 64 bits. (ADBailey a signalé cela). "Sysnative" n'est requis que pour lancer des applications 64 bits à partir d'un hôte de script 32 bits (par exemple, un processus de construction Visual Studio ou l'invocation de script à partir d'une autre application 32 bits).

  • Pour rendre plus claire la façon dont les paramètres sont interprétés, je l’affiche maintenant comme _P1=value1 P2=value2 ... P9=value9_. Ceci est particulièrement utile si vous devez inclure des paramètres tels que des chemins entre guillemets, par exemple. _"C:\Program Files"_.

Liens utiles:

306
Matt

Comme jcoder et Matt l'ont mentionné, PowerShell a simplifié la tâche. Il était même possible de l'intégrer au script batch sans créer de nouveau script.

J'ai modifié le script 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 

Le libellé :getPrivileges n'est pas nécessaire.

33
Ir Relevant

J'utilise l'excellente réponse de Matt, mais je vois une différence entre mes systèmes Windows 7 et Windows 8 lorsque j'exécute des scripts élevés.

Une fois le script élevé dans Windows 8, le répertoire actuel est défini sur C:\Windows\system32. Heureusement, il existe une solution de contournement simple en remplaçant le répertoire en cours par le chemin du script en cours:

cd /d %~dp0

Remarque: utilisez cd /d pour vous assurer que la lettre du lecteur a également été modifiée.

Pour tester cela, vous pouvez copier ce qui suit dans un script. Exécutez normalement sur l'une ou l'autre version pour voir le même résultat. Exécuter en tant qu'administrateur et voir la différence dans 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
Stephen Klancher

Je le fais de cette façon:

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 cette façon, c'est simple et utilisez uniquement les commandes par défaut de Windows. C’est génial si vous devez redistribuer votre fichier de commandes.

CD /d %~dp0 Définit le répertoire actuel sur le répertoire actuel du fichier (s'il ne l'est pas déjà, quel que soit le lecteur dans lequel se trouve le fichier, grâce à l'option /d.).

%~nx0 Renvoie le nom de fichier actuel avec l'extension (si vous n'incluez pas l'extension et qu'il y a un exe portant le même nom dans le dossier, il l'appellera).

Il y a tellement de réponses sur ce post que je ne sais même pas si ma réponse sera vue.

Quoi qu'il en soit, je trouve ce moyen plus simple que les autres solutions proposées dans les autres réponses, j'espère que cela aidera quelqu'un.

25
Matheus Rocha

Matt a une excellente réponse, mais elle élimine tous les arguments transmis au script. Voici ma modification qui conserve les arguments. J'ai également incorporé le correctif de Stephen pour le problème du répertoire de travail dans 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...
22
jxmallett

J'utilise PowerShell pour relancer le script élevé si ce n'est pas le cas. Placez ces lignes tout en haut de votre script.

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

J'ai copié la méthode 'net name' de la réponse de @ Matt. Sa réponse est beaucoup mieux documentée et comporte des messages d'erreur, etc. Celui-ci présente l’avantage que PowerShell est déjà installé et disponible sous Windows 7 et versions supérieures. Pas de fichiers VBScript (* .vbs) temporaires, et vous n'avez pas à télécharger d'outils.

Cette méthode devrait fonctionner sans configuration ni installation, tant que vos autorisations d'exécution PowerShell ne sont pas verrouillées.

19
Ryan Bemrose

Pour certains programmes, définissez la variable d’environnement super secret __COMPAT_LAYER sur RunAsInvokerfonctionnera .

set "__COMPAT_LAYER=RunAsInvoker"
start regedit.exe

Bien que ce soit le cas, aucun UAC ne sera invité à indiquer à l’utilisateur de continuer sans les autorisations de l’administrateur.

13
npocmaka

J'ai collé ceci au début du 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
TanisDLJ

Bien que cela ne soit pas directement applicable à cette question, parce qu'il souhaite obtenir des informations pour l'utilisateur, Google m'a amené ici lorsque je souhaitais exécuter mon fichier .bat avec le planificateur de tâches.

L’approche la plus simple a consisté à créer un raccourci vers le fichier .bat, car vous pouvez définir Run as administrator directement à partir des propriétés avancées.

L'exécution du raccourci à partir du planificateur de tâches exécute le fichier .bat élevé.

0
Hugo Delsing