web-dev-qa-db-fra.com

Pourquoi «Exécuter en tant qu'administrateur» modifie (parfois) le répertoire actuel du fichier de commandes?

J'ai un fichier batch qui se trouve dans le même répertoire que le fichier que je veux xcopy. Mais pour une raison quelconque, le fichier n'est pas trouvé.

Je pensais que le répertoire actuel était toujours là où se trouvait le fichier de commandes.

J'exécute le fichier de commandes en tant qu'administrateur. Cela se produit sur un ordinateur de bureau Windows 7 64 bits.

Fichier batch:

@ECHO OFF
XCOPY /y "File1.txt" "File2.txt"
PAUSE

Erreur:

File not found - File1.txt
0 File(s) copied
14
mcu

Quel répertoire est le répertoire de travail actuel lors du démarrage d'un fichier de commandes avec l'élément de menu contextuel Exécuter en tant qu'administrateur dépend de Contrôle de compte d'utilisateur (UAC) pour l'utilisateur actuel.

Cela peut être démontré avec le petit fichier de commandes suivant C:\Temp\Test.bat:

@echo Current directory is: %CD%
@pause

Après avoir sélectionné dans Paramètres de contrôle de compte d'utilisateur

Par défaut - M'avertir uniquement lorsque des programmes tentent d'apporter des modifications à mon ordinateur

  • Ne me prévenez pas lorsque je modifie les paramètres de Windows

et en utilisant Exécuter en tant qu'administrateur , Windows utilise la clé de registre

HKEY_CLASSES_ROOT\batfile\Shell\runasuser\command

Cette clé de Registre ne contient pas de chaîne par défaut pour exécuter le fichier de commandes. Au lieu de cela, il y a la valeur de chaîne DelegateExecute avec le CLSID {ea72d00e-4960-42fa-ba92-7792a7944c1d}.

Le résultat ouvre une fenêtre de dialogue avec le titre Contrôle de compte d'utilisateur et le texte:

Voulez-vous autoriser le programme suivant à apporter des modifications à cet ordinateur?

Nom du programme: Processeur de commandes Windows
Éditeur vérifié: Microsoft Windows

Après confirmation par l'utilisateur, Windows ouvre temporairement une nouvelle session utilisateur comme lors de l'utilisation en ligne de commande RunAs .

Dans cette nouvelle session utilisateur, le répertoire de travail actuel est %SystemRoot%\System32 en exécutant maintenant la commande définie dans le registre Windows avec la chaîne de clé par défaut

HKEY_CLASSES_ROOT\batfile\Shell\runas\command

lequel est:

%SystemRoot%\System32\cmd.exe /C "%1" %*

Par conséquent, une fenêtre de console est ouverte avec le titre C:\Windows\System32\cmd.exe et les 2 lignes:

Current directory is: C:\Windows\System32
Press any key to continue . . .

Après avoir appuyé sur une touche, l'exécution par lots se termine, ce qui entraîne la fermeture de cmd.exe ce qui entraîne la fermeture de la session utilisateur.


Mais après avoir sélectionné dans Paramètres de contrôle de compte d'utilisateur

Ne m'informez jamais quand

  • Les programmes essaient d'installer des logiciels ou d'apporter des modifications à mon ordinateur

  • Je modifie les paramètres de Windows

le comportement est différent car l'utilisateur a déjà des privilèges élevés.

Maintenant, Windows utilise directement la commande

%SystemRoot%\System32\cmd.exe /C "%1" %*

selon la chaîne de clé par défaut

HKEY_CLASSES_ROOT\batfile\Shell\runas\command

dans la session utilisateur en cours.

Le résultat ouvre une fenêtre de console également avec le titre C:\Windows\System32\cmd.exe , mais affiché dans la fenêtre est:

Current directory is: C:\Temp
Press any key to continue . . .

Le répertoire de travail actuel du processus parent (l'Explorateur Windows en tant que bureau) est utilisé pour exécuter le fichier de commandes car aucun basculement vers une session utilisateur différente n'était nécessaire dans ce cas.


PA a déjà publié 2 solutions possibles dans sa réponse que je reproduis ici avec une petite amélioration (pushd avec répertoire entre guillemets) et avec en ajoutant un troisième.

  1. Remplacez le répertoire actuel par le répertoire du fichier de commandes en utilisant pushd et popd :

    pushd "%~dp0"
    %SystemRoot%\System32\xcopy.exe "File1.txt" "File2.txt" /Y
    popd
    

    Cela fonctionne également pour les chemins UNC. Exécuter dans une fenêtre d'invite de commandes pushd /? pour une explication pourquoi cela fonctionne également pour les chemins UNC.

  2. Utilisez le répertoire du fichier de commandes dans les spécifications source et destination:

    %SystemRoot%\System32\xcopy.exe "%~dp0File1.txt" "%~dp0File2.txt" /Y
    
  3. Changez le répertoire de travail en répertoire du fichier de commandes en utilisant cd :

    cd /D "%~dp0"
    %SystemRoot%\System32\xcopy.exe "File1.txt" "File2.txt" /Y
    

    Cela ne fonctionne pas pour les chemins UNC car l'interpréteur de commandes cmd ne prend pas en charge un chemin UNC comme répertoire actuel par défaut, voir par exemple CMD ne ne prend pas en charge les chemins UNC en tant que répertoires actuels pour plus de détails.

21
Mofi

Le message d'erreur est très explicite. Le fichier file1.txt n'est pas trouvé.

Le nom de fichier n'incluant pas de chemin absolu, le système essaie de le trouver dans le répertoire en cours. Votre répertoire actuel ne contient pas ce fichier.

Votre idée fausse est que le répertoire courant n'est pas le répertoire qui contient le fichier bat. Ce sont deux concepts sans rapport.

Vous pouvez facilement vérifier en ajoutant ces deux commandes dans votre fichier bat

echo BAT directory is %~dp0
echo Current directory  is %CD%

vous pouvez remarquer qu'ils sont différents et qu'il y a une différence subtile dans la façon dont la dernière barre oblique inverse est ajoutée ou non.

Donc, il existe essentiellement deux façons de faire face à ce problème

  1. soit changer le répertoire courant pour qu'il corresponde à celui attendu

    pushd %~dp0
    XCOPY /y "File1.txt" "File2.txt"
    popd
    
  2. ou spécifiez le chemin complet dans la commande

    XCOPY /y "%~dp0File1.txt" "%~dp0File2.txt"
    
2
PA.

Par souci d'exhaustivité et d'obscurité, j'ajoute une autre solution de contournement, confirmée comme fonctionnant sous Windows 8.1 et censée fonctionner ailleurs, car elle repose sur des fonctionnalités documentées:

Vous pouvez modifier les clés de définition de la commande runas
_HKEY_CLASSES_ROOT\batfile\Shell\runas\command_ et
_HKEY_CLASSES_ROOT\cmdfile\Shell\runas\command_ en

_%SystemRoot%\System32\cmd.exe /S /C "(for %%G in (%1) do cd /D "%%~dpG") & "%1"" %*
_

Ce qui entraîne le démarrage du fichier bat ou cmd dans son répertoire conteneur lors de l'utilisation du verbe runas, respectivement l'entrée de menu "Exécuter en tant qu'administrateur".

Que font exactement les ajouts à la commande d'origine:

  • _cmd /S_ supprime la première et la dernière (double) citation dans la chaîne de commande après _/C_
  • for %%G in (%1) do énumère sa seule entrée, l'argument _%1_, le rendant disponible pour l'expansion sous la forme _%%G_ dans le corps de la boucle; la lettre est arbitraire mais certains peuvent être "réservés"
  • _%%~dpG_ se développe en d rive et p ath de _%%G_, le ~ tilde supprimant les guillemets si présent, c'est pourquoi nous les rajoutons explicitement
  • _cd /D_ change à la fois le d rive et le répertoire en son argument, et enfin
  • _&_ exécute la deuxième commande _"%1" %*_ quelle que soit la réussite de la première.

Vous pouvez utiliser pushd qui supportera même les chemins UNC, mais un popd errant entraînerait n'importe quel script dans le _system32_ répertoire, pas un comportement que j'aimerais.

Il devrait être également possible pour l'entrée exefile, mais franchement, je préfère vivre avec l'incohérence que de essayez ceci sur mon système, car toute erreur pourrait casser beaucoup.

Profitez de la défaite des mécanismes de sécurité de votre système d'exploitation :)

1
Viktor Svub