Remerciements▲
Je tiens à remercier gangsoleil et djibril pour leurs relectures techniques attentives ; ainsi que f-leb, FRANOUCH et milkoseck pour leurs relectures orthographiques assidues.
I. Introduction▲
Les fichiers batch sont des scripts de commandes qui s'exécutent dans l'interpréteur de commande Windows. Alors quelle pourrait être l'utilité d'écrire ces lignes de commande dans un script ? En fait, il y a plusieurs raisons :
- éviter d'avoir à réécrire sans cesse les mêmes commandes lors de tâches répétitives ;
- possibilité de créer de vrais petits « programmes » facilitant les tâches qui doivent être réalisées via l'interpréteur de commande.
Nous allons étudier les méthodes permettant de concevoir ces programmes. Dans la suite de ce document, nous utiliserons la convention suivante :
- les termes « interpréteur de commande » ou « interpréteur » désignent l'exécutable « cmd.exe » ;
- dans les syntaxes de commande, les parties encadrées avec les caractères [ et ] sont optionnelles, les parties encadrées avec les caractères < et > sont à remplacer par différentes informations à fournir (celles-ci seront précisées avec la syntaxe) et les parties encadrées avec les caractères { et } sont des parties à choix multiple où chaque choix est séparé des autres avec le caractère |.
La convention syntaxique est la même que celle pour l'aide en ligne de commande, ainsi il vous sera plus facile de la comprendre.
I-A. L'éditeur de texte▲
Un simple éditeur de texte, tel que le notepad fourni avec Windows, s'avère peu pratique pour écrire des scripts batch. Pour exploiter pleinement les capacités de ces scripts, il faut pouvoir encoder les caractères non anglophones comme les accents ou le « ç » afin de pouvoir accéder aux fichiers dont les noms comportent ce genre de caractères exotiques. La plupart des éditeurs de texte encodent les fichiers avec le jeu de caractères de la norme ANSI qui correspond à la langue d'installation du système d'exploitation alors que l'interpréteur de commande utilise une étendue de page de code (aussi appelée Code Page) comme CP-850 qui est l'encodage par défaut de l'interpréteur pour les systèmes Windows installés en français (France).
Sous Windows, il y a un éditeur de texte que vous possédez peut-être déjà, c'est Notepad++ (la version 5.9.x ou supérieure est nécessaire) ; si ce n'est pas le cas, vous pouvez le télécharger ici : http://notepad-plus-plus.org/fr/
Dans ce dernier, vous pourrez sélectionner l'encodage dans le menu « Encodage > Codage de caractères », puis si vous voulez le français par exemple : sélectionnez « Langues d'Europe occidentale>OEM-850 ». Dans Notepad++, les code pages sont appelés OEM-XXX, au lieu de CP-XXX, mais ce sont les mêmes encodages. Il faut aussi noter que dans le même menu que OEM-850, il y a OEM-863 : français ; c'est bien du français, mais pour le Québec. Cette manipulation sera à effectuer pour chaque script afin d'utiliser le bon encodage, il n'est pas possible d'effectuer cette opération de façon automatique dans Notepad++.
I-B. Encodage des caractères▲
La gestion des pages de code dans l'interpréteur se fait via les commandes mode et chcp, ces commandes permettent d'afficher le code page utilisé ou de le modifier en utilisant l'une des syntaxes suivantes.
Définir le code page à utiliser (où <XXX> est le numéro de code page).
mode
con
cp select=<
XXX>
chcp
<
XXX>
Afficher le code page utilisé.
mode
con
cp [/status]
chcp
Le tableau 1 fournit une liste non exhaustive des différents code pages utilisés par l'interpréteur de commande.
Code Page |
Description |
CP-437 |
pour le support des langues anglophones. |
CP-720 |
pour le support des langues arabes. |
CP-737 |
pour le support du grec. |
CP-775 |
pour le support des langues baltes. |
CP-850 |
pour le support des langues d'Europe occidentale (mis à jour par CP 858) dont le français (France), mais aussi l'allemand, le basque, le catalan, le danois, l'espagnol, le finnois, l'italien, le néerlandais, le norvégien, le portugais, le suédois, l'afrikaans, le faeroese, le flamand et l'irlandais. |
CP-852 |
pour le support des langues d'Europe centrale. |
CP-855 |
pour le support de l'alphabet cyrillique. |
CP-857 |
pour le support du turc. |
CP-858 |
pour le support des langues d'Europe occidentale, dont le français (France), il s'agit d'une mise à jour de 1998 basée sur CP-850 ou seul le symbole « € » a été ajouté au point de code 0xD5. |
CP-861 |
pour le support de l'islandais. |
CP-863 |
pour le support du français (Québec). |
CP-864 |
pour le support des langues arabes. |
CP-866 |
pour le support de l'alphabet cyrillique. |
CP-869 |
pour le support du grec. |
Tableau 1 : Code Page
Il faut noter que malgré la mise à jour de CP-850 par CP-858, l'encodage par défaut pour l'interpréteur sous Windows installé en français (France) reste CP-850. Il est donc préférable d'utiliser CP-850. La liste des code pages supportés par un système Windows est disponible dans le registre sous la clé : HKLM\SYSTEM\CurrentControlSet\Control\Nls\CodePage. Vous noterez la présence de 1252 dans la clé précédemment mentionnée, il s'agit de l'encodage Windows-1252 utilisé par les API Windows et donc par la plupart des blocs-notes sur les systèmes installés en français (France) ; cependant il n'est pas recommandé de l'utiliser dans l'interpréteur de commande du fait que les commandes ne sont pas faites pour le supporter, il peut même provoquer des bogues dans certains cas. L'encodage par défaut pour l'interpréteur peut être trouvé via la valeur OEMCP de type REG_SZ toujours dans la clé précédemment mentionnée.
Il est possible d'ajouter les Code Pages manquants (par défaut, seuls les code pages correspondant à la langue d'installation sont disponibles) en installant le pack de langue correspondant à l'encodage voulu. Chaque pack de langue comprend une table de conversion permettant à Windows de convertir un point de code en sa représentation graphique et donc de l'afficher. Il est aussi possible d'utiliser l'Unicode (UCS-2 seulement) avec les sorties de commandes internes de l'interpréteur pour permettre l'interaction avec les programmes qui utilisent Unicode de manière « native » (pour Perl par exemple) en appelant l'interpréteur via la commande suivante (où <commande> est la commande à exécuter).
cmd
/u <
commande>
Dans Notepad++, les caractères absents du clavier peuvent êtres rajoutés via le menu « Édition>Panneau des caractères ASCII » puis double-clic sur le caractère voulu.
I-C. Hello World▲
Nous allons commencer par le traditionnel « hello world », dont voici le code (Script 1). Copiez le code dans Notepad++, puis enregistrez-le avec l'encodage OEM-850 et l'extension « .bat » ou « .cmd » (seulement sur les systèmes Vista et supérieur).
La différence entre fichier « .bat » et fichier « .cmd » tient dans l'interpréteur de commande, en effet sur les systèmes Windows XP, il y avait deux interpréteurs de commande : « cmd.exe » et « COMMAND.COM ». Les fichiers avec l'extension « .bat » étaient exécutés par « cmd.exe » et les fichiers avec l'extension « .cmd » étaient exécutés par « COMMAND.COM », les deux types de fichiers avaient des spécificités différentes. Cependant sur les systèmes Windows Vista et supérieur, seul subsiste « cmd.exe », tous les scripts « .bat » et « .cmd » sont exécutés par « cmd.exe » et ont les mêmes spécificités.
2.
3.
4.
5.
6.
7.
8.
9.
@
echo
off
cls
rem Ceci est une remarque.
:: Ceci est un commentaire.
echo
Hello World !!!! Déjà là ?
echo.
pause
Lorsqu'on exécute ce script en cliquant dessus, on obtient l'affichage suivant.
Étudions la composition du script 1. La première ligne, @echo off, est déjà intéressante, elle est composée :
- du préfixe @ qui sert à inverser l'état de l'affichage standard ;
- de la commande echo qui sert à gérer l'affichage en ligne de commande ;
- et du paramètre off qui sert à désactiver l'affichage standard.
L'affichage standard définit ce que l'interpréteur de commande affiche par défaut. Par exemple lors du lancement de l'interpréteur de commande ci-dessous ; l'affichage standard renvoie le chemin d'accès du répertoire courant, soit C:\Users\Portable>.
Lors de l'exécution d'un script, l'affichage standard renvoie, par défaut, le chemin d'accès du répertoire courant suivi de la commande en cours d'exécution comme dans l'exemple ci-dessous.
Le préfixe @, quand il est placé en début de ligne, sert à inverser l'état de l'affichage standard (activé ou désactivé) pour l'exécution de la commande qui le suit (pas seulement pour la commande echo). Ce comportement prend fin une fois la commande exécutée. Ainsi la commande @cd /d "C:\Users\Portable\" ne sera affichée que si l'affichage standard est désactivé. La syntaxe du préfixe @ est la suivante (où <commande> est la commande à exécuter) :
@
<
commande>
La commande echo gère l'affichage dans l'interpréteur, elle peut :
- modifier l'état de l'affichage standard ;
- afficher l'état de l'affichage standard ;
- afficher un message ou une ligne vide dans l'interpréteur.
Désactiver l'affichage standard peut être fait via la syntaxe suivante (seuls les erreurs et les messages de la commande echo sont affichés).
echo
off
Activer l'affichage standard peut être fait via la syntaxe suivante (tout est affiché).
echo
on
Utilisée sans paramètre, la commande echo renvoie l'état de l'affichage standard en cours.
echo
Si l'on reprend le script 1, la ligne @echo off permet de désactiver l'affichage standard sans que la commande soit affichée. Sur la deuxième ligne du script 1, la commande cls est utilisée pour vider la fenêtre de l'interpréteur de son contenu, cette commande ne prend aucun paramètre. Sa syntaxe est donc la suivante.
cls
La ligne suivante du script 1 est vide, elle ne sera donc pas prise en compte lors de l'exécution du script permettant ainsi de le rendre plus lisible. La quatrième ligne est composée de la commande rem et d'une chaîne de caractères, cette commande permet d'insérer des remarques dans son script. Si et seulement si l'affichage standard est activé, la commande rem sera affichée. La syntaxe de la commande rem est la suivante (où <remarque> est la chaîne de caractères à insérer en remarque).
rem <remarque>
La cinquième ligne du script 1, :: Ceci est un commentaire., est composée du préfixe :: et d'une chaîne de caractères. Le préfixe :: définit la chaîne de caractères qui le suit comme étant un commentaire ; ce comportement prend fin au premier retour à la ligne. Quel que soit l'état de l'affichage standard, la chaîne de caractères préfixée par :: ne sera pas affichée. La syntaxe est la suivante (où <commentaire> est le commentaire à insérer).
::<commentaire>
Suit une autre ligne vide puis la commande echo Hello World !!!! Déjà là ? qui affiche Hello World !!!! Déjà là ? dans la fenêtre de l'interpréteur. La syntaxe suivante permet donc d'afficher un message même si l'affichage standard est désactivé (où <message> est le message à afficher).
echo
<
message>
Vient ensuite la commande echo. qui permet d'afficher la ligne vide que l'on voit dans l'affichage obtenu. Si un point suit directement la commande echo et qu'après le point il y a un retour à la ligne, celle-ci affiche une ligne vide.
echo.
Sur la ligne suivante se trouve la commande pause qui met en pause l'exécution du script jusqu'à ce que l'utilisateur appuie sur une touche quelconque du clavier, elle affiche le message Appuyez sur une touche pour continuer… (quel que soit l'état en cours de l'affichage standard) et ne prend aucun paramètre. Dans le script 1, cette commande permet de visualiser le contenu de la fenêtre de l'interpréteur avant que celle-ci ne se ferme.
pause
I-D. Différence entre la commande « rem » et le préfixe « :: »▲
Pour mieux comprendre la différence entre la commande rem et le préfixe ::, étudions le script 2.
2.
3.
4.
5.
6.
7.
cls
rem Remarque 1
:: Commentaire 1
@
echo
off
rem Remarque 2
:: Commentaire 2
pause
Comme vous le voyez dans l'affichage du script 2, la commande rem Remarque 1, est présente à l'écran ; l'affichage standard étant activé, les commandes exécutées sont toutes affichées. La chaîne :: Commentaire 1 n'est pas affichée, c'est dû au fait que le préfixe :: n'est pas une commande et donc n'est pas renvoyé par l'affichage standard. Vient ensuite la commande @echo off qui désactive l'affichage standard sans que la commande ne soit affichée, suivie de la commande rem Remarque 2 qui n'est pas affichée (l'affichage standard étant à présent désactivé), pas plus que la chaîne :: Commentaire 2 qui est de toute façon exclue par l'affichage standard.
I-E. Les échappements de caractères▲
Les caractères spéciaux (aussi appelé méta-caractères) sont des caractères ayant une signification particulière pour l'interpréteur de commande. Chaque fois que l'interpréteur les rencontre, il leur applique le traitement correspondant, quelles que soient leurs positions dans la ligne de commande. En voici la liste :
&
|
^ <
>
( )
Pour pouvoir les utiliser dans une chaîne de caractères, il faut les « échapper » avec le caractère ^, c'est-à-dire placer ce caractère devant. Ainsi l'interpréteur saura qu'ils font partie d'une chaîne et qu'ils n'ont rien à voir avec la commande. Voir le script 3 comme exemple.
2.
3.
@
echo
off
echo
^&
^|
^^ ^<
^>
^( ^)
pause
Il faut aussi noter que d'autres caractères peuvent avoir besoin d'être échappés, mais ils suivent d'autres règles. Le caractère % doit être échappé par lui-même pour être considéré comme faisant partie d'une chaîne lorsqu'il est utilisé dans un script comme le montre le script 4.
2.
3.
@
echo
off
echo
%%
pause
Un autre cas est à noter, il concerne les caractères " et \ qui doivent être échappés avec le caractère \ lorsqu'ils sont utilisés dans un paramètre entre guillemets. Dans le script 5, si les caractères " et \ n'avaient pas été échappés, le résultat ne serait pas celui attendu. L'interpréteur aurait compris qu'il y avait une chaîne "cmd /c "notepad et une chaîne "%cd%\%~nx0"", ce qui aurait conduit à une erreur d'évaluation de la commande.
2.
3.
@
echo
off
runas
/User:%UserName%
"cmd
/c \"notepad \"%cd%
\\%~nx0
\""
pause
Le caractère d'échappement ^ peut aussi être utilisé afin d'écrire une seule et même commande sur plusieurs lignes. Lorsque l'interpréteur rencontre le caractère ^ devant un retour à la ligne, il supprime ce caractère et le retour à la ligne, puis continue à lire les données fournies dans la ligne suivante. Exemple avec le script 6.
2.
3.
4.
@
echo
off
echo
foo ^
bar
pause
D'autres échappements existent, cependant ils seront abordés plus loin dans ce document, car ils s'appliquent à des commandes précises et ne concernent pas les autres. Par exemple, la commande echo qui considère le point comme un échappement de la chaîne qui le suit. Lorsque la commande echo est directement suivie d'un point, elle considère la chaîne qui se trouve après le point comme une chaîne à afficher, retour à la ligne compris. Cela permet d'utiliser en début de chaîne un « mot » qu'elle aurait, dans le cas contraire, considéré comme un paramètre. Exemple avec le script 7.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
@
echo
off
echo
Affichage d'une ligne vide sans le point:
echo
echo.
echo.
echo
Affichage d'une ligne vide avec le point:
echo.
echo.
echo.
echo
Affichage de la chaîne " /?
Print
this help." sans le point:
echo
/?
Print
this help.
echo.
echo.
echo
Affichage de la chaîne " /?
Print
this help." avec le point:
echo.
/?
Print
this help.
echo.
echo.
pause
Dans l'affichage ci-dessous, on remarque que l'affichage d'une ligne vide sans le point renvoie l'état de l'affichage standard contrairement à celle avec un point. Si à la place d'un retour à la ligne, nous avions utilisé plusieurs espaces, le résultat aurait été le même, quel que soit le nombre d'espaces. En effet, l'interpréteur n'évalue pas le nombre d'espaces entre deux constituantes d'une ligne de commande, il aurait donc considéré qu'aucun paramètre n'est fourni. Il en est de même avec la chaîne /? Print this help., l'interpréteur considère la sous-chaîne /? comme un paramètre qui affiche l'aide en ligne de commande.
Le tableau 2 récapitule les différents échappements que l'on vient de voir.
Échappement |
Description |
^ |
Placé devant un des caractères spéciaux, soit &^|<>(), il signifie que le caractère qui le suit fait partie de la chaîne et qu'il n'a rien à voir avec la commande. |
Placé devant un retour à la ligne, il signifie que la ligne suivante fait partie de la même commande. |
|
% |
Placé devant le caractère % dans un script, il signifie que le caractère qui le suit fait partie de la chaîne. |
\ |
Placé devant un des caractères \ et " dans un paramètre entre guillemets, il signifie que le caractère qui le suit fait partie du paramètre entre guillemets. |
. |
Placé immédiatement après la commande echo, il signifie que la chaîne qui le suit est une chaîne à afficher et non un paramètre de la commande echo. |
Tableau 2 : Les échappements
I-F. Les bogues de la commande « rem » et du préfixe « :: »▲
Un problème récurrent en batch scripting est que les remarques et commentaires (fournis via rem et ::) provoquent des bogues du fait d'une mauvaise utilisation. La commande rem et le préfixe :: prenant en paramètre une chaîne de caractères, ils attendent donc une chaîne de caractères valide. Ainsi les échappements doivent être utilisés pour les caractères suivants : & | ^ < > ( ) %.
II. Les variables▲
Les variables sont gérées via la commande set, il en existe deux types dont voici la liste :
- les variables de type chaîne de caractères ;
- les variables de type nombre entier signé.
Chaque type de variable est déclaré et traité différemment ; il est possible de les transtyper, c'est-à-dire de les passer d'un type à l'autre, à condition de respecter les règles suivantes :
- une chaîne de caractères ne pouvant être transtypée en nombre que si elle ne contient que des chiffres ;
- un nombre pourra toujours être transtypé en chaîne de caractères (qui ne contiendra que des chiffres).
Les variables sont accessibles via un identifiant qui leur est propre. Ainsi chaque fois qu'il sera fait mention de cet identifiant, l'interpréteur sera capable de fournir la valeur associée. Cela peut être fait en utilisant le caractère % de part et d'autre de l'identifiant, on parle alors d'expansion de la variable. Par exemple avec la variable PATH (qui est une variable d'environnement : fournie par le système), pour obtenir sa valeur ; il faudrait utiliser la syntaxe %PATH% comme dans le script 8.
2.
3.
@
echo
off
echo
%PATH%
pause
Ce qui produirait un affichage similaire à celui ci-dessous.
Les identifiants de variable ne sont pas sensibles à la casse, c'est-à-dire que l'interpréteur ne fait pas la différence entre majuscule et minuscule lorsqu'il expanse, crée ou modifie une variable. Ainsi les identifiants PATH, Path et path désignent une seule et même variable.
II-A. Les variables d'environnement▲
Nous allons commencer par les variables d'environnement, elles sont toutes de type chaîne de caractères. C'est le système d'exploitation qui définit leurs valeurs soit parce que d'un système à l'autre leurs valeurs peuvent être différentes, soit parce que ces variables sont définies dynamiquement lors de leur expansion. Cela permet d'accéder à des fichiers dont le chemin d'accès pourrait ne pas être le même d'une machine à l'autre ou de pouvoir obtenir, par exemple, l'heure ou la date dont les valeurs sont modifiées en permanence.
Dans le tableau 3, vous trouverez la liste de ces variables. Dans la première colonne, vous trouverez le nom de ces variables ; ces noms sont réservés par le système, et même s'il est possible de modifier leur valeur pendant l'exécution du script, les modifications apportées prendront fin en même temps que le script. De plus, dans certains cas, la modification de leur valeur peut entraîner une mauvaise évaluation de l'environnement par le script. La deuxième colonne donne la description de la valeur renvoyée par la variable. Et enfin, la troisième colonne donne la compatibilité en fonction de la version du système. Dans cette colonne, il est fait mention de « systèmes NT », il s'agit de la « famille » du système d'exploitation. Les systèmes NT sont des systèmes reposant sur un noyau de type NT provenant du système Windows du même nom. Cette famille comprend au jour où sont écrites ces lignes :
- les systèmes Windows NT X.Y (où X est la révision majeure et Y la révision mineure) ;
- les systèmes Windows 2000 ;
- les systèmes Windows XP ;
- les systèmes Windows Server 2003 ;
- les systèmes Windows Vista ;
- les systèmes Windows Server 2008 ;
- les systèmes Windows 7 ;
- les systèmes Windows Server 2012 ;
- et les systèmes Windows 8.
Variable |
Description |
Compatibilité |
AllUsersProfile |
Renvoie le chemin d'accès complet jusqu'au répertoire des données utilisateurs communes à tous les utilisateurs. Par défaut : %HomeDrive%\ProgramData. |
Disponible sur tous les systèmes NT. |
AppData |
Renvoie le répertoire commun des données d'application sous la forme |
Disponible sur tous les systèmes NT. |
CD |
Renvoie le chemin d'accès complet jusqu'au répertoire dans lequel le script est en train de s'exécuter, ce répertoire peut être différent du répertoire dans lequel le script s'est lancé (si celui-ci a été « relocalisé » au cours de son exécution). |
Disponible sur tous les systèmes NT. |
CmdCmdLine |
Renvoie la ligne de commande originale qui a appelé l'interpréteur de commande. |
Disponible sur tous les systèmes NT. |
CmdExtVersion |
Renvoie le numéro de version des extensions de commande du processeur de commande en cours. |
Disponible sur tous les systèmes NT. |
CommonProgramFiles |
Renvoie le chemin d'accès complet jusqu'au répertoire des fichiers communs aux applications 32bits sur les systèmes 32bits ou aux applications 64bits sur les systèmes 64bits, soit : %ProgramFiles%\Common Files. |
Disponible sur tous les systèmes NT. |
CommonProgramFiles(x86) |
Renvoie le chemin d'accès complet jusqu'au répertoire des fichiers communs aux applications 32bits sur les systèmes 64bits : |
Disponible uniquement sur les systèmes NT 64bits. |
CommonProgramW6432 |
Renvoie le chemin d'accès complet jusqu'au répertoire des fichiers communs aux applications 16 bits sur les systèmes 64bits et sur les systèmes 32bits Vista et supérieur : %ProgramW6432%\Common Files. |
Disponible uniquement sur les systèmes NT 64bits et sur les systèmes 32bits Vista et supérieur. |
ComputerName |
Renvoie le nom de l'ordinateur sur lequel le script est en train de s'exécuter. |
Disponible sur tous les systèmes NT. |
ComSpec |
Renvoie le chemin d'accès complet vers l'interpréteur : %WinDir%\system32\cmd.exe. |
Disponible sur tous les systèmes NT. |
Date |
Renvoie la date actuelle en utilisant le même format que la commande date. |
Disponible sur tous les systèmes NT. |
ErrorLevel |
Renvoie la valeur du code d'erreur en cours. Cette valeur est modifiée après chaque ligne de commande, en fonction du résultat de la commande. En général, la variable ErrorLevel renvoie 1 ou plus en cas d'erreur de la dernière commande et 0 si aucune erreur ne s'est produite. Cependant, il arrive que ce comportement varie selon les commandes, il est donc recommandé de se reporter à l'aide concernant ladite commande. |
Disponible sur tous les systèmes NT. |
FP_NO_HOST_CHECK |
N.C. |
Disponible sur tous les systèmes NT. |
HighestNumaNodeNumber |
Renvoie le numéro de nœud NUMA le plus élevé de l'ordinateur sur lequel s'exécute le script. |
Disponible uniquement sur les systèmes NT 64bits. |
HomeDrive |
Renvoie le point de montage de la partition qui accueille les répertoires utilisateurs. Par défaut : C:. |
Disponible sur tous les systèmes NT. |
HomePath |
Renvoie le chemin d'accès vers le répertoire de l'utilisateur actuellement logué. Par défaut : \Users\%UserName%. |
Disponible sur tous les systèmes NT. |
LocalAppData |
Renvoie le répertoire local des données d'application sous la forme : %UserProfile%\AppData\Local. |
Disponible sur tous les systèmes NT. |
LogonServer |
Renvoie l'URL locale du système d'exploitation sous la forme \\%ComputerName%. |
Disponible sur tous les systèmes NT. |
Number_Of_Processors |
Renvoie le nombre de cœurs logiques de l'ordinateur sur lequel le script est en train de s'exécuter. |
Disponible sur tous les systèmes NT. |
OS |
Renvoie le type de noyau sur lequel repose le système d'exploitation, sur les systèmes NT cette variable renverra toujours la chaîne Windows_NT. |
Disponible sur tous les systèmes NT. |
Path |
Renvoie la liste des répertoires reconnus par le système comme contenant des exécutables, chaque répertoire est listé par son chemin d'accès complet suffixé par un point-virgule. Si un exécutable se trouve dans un des répertoires de cette liste, il n'est pas nécessaire de fournir un chemin d'accès complet pour l'appeler en ligne de commande. |
Disponible sur tous les systèmes NT. |
PathExt |
Renvoie la liste des extensions de fichier reconnues par le système comme étant des extensions de fichiers exécutables. Si une extension de fichier ne figure pas dans cette liste, alors le fichier ne peut être appelé en tant que commande. |
Disponible sur tous les systèmes NT. |
Processor_Architecture |
Renvoie le type d'architecture (32/64bits) du processeur sur lequel s'exécute le script. Les valeurs possibles sont : X86 pour les processeurs 32bits, AMD64 pour les processeurs 64bits basés sur l'architecture x86 et IA64 pour les processeurs Itanium. |
Disponible sur tous les systèmes NT. |
Processor_Identifier |
Renvoie une identification précise du processeur sur lequel s'exécute le script. Cette identification est une chaîne de caractères composée du type d'architecture suivi de la famille, du modèle, de la révision et enfin du fabricant du processeur. |
Disponible sur tous les systèmes NT. |
Processor_Level |
Renvoie une identification précise de la famille de la micro-architecture du processeur. |
Disponible sur tous les systèmes NT. |
Processor_Revision |
Renvoie une identification précise du modèle et de la révision du processeur sous forme d'une chaîne de caractères représentant un nombre hexadécimal sur deux octets, le premier correspond au modèle et le second à la révision. |
Disponible sur tous les systèmes NT. |
ProgramData |
Renvoie le répertoire commun des données d'application 64bits sous la forme : %SystemDrive%\ProgramData. |
Disponible uniquement sur les systèmes NT 64bits. |
ProgramFiles |
Renvoie le chemin d'accès complet vers le répertoire « ProgramFiles ». Ce répertoire contient les applications 32bits sur les systèmes 32bits ou les applications 64bits sur les systèmes 64bits. |
Disponible sur tous les systèmes NT. |
ProgramFiles(x86) |
Renvoie le chemin d'accès complet vers le répertoire « ProgramFiles(x86) ». Ce répertoire contient les applications 32bits sur les systèmes 64bits. |
Disponible uniquement sur les systèmes NT 64bits. |
ProgramW6432 |
Renvoie le chemin d'accès complet vers le répertoire « ProgramW6432 ». Ce répertoire contient les applications 16bits sur les systèmes 64bits et sur les systèmes 32bits Vista et supérieur. |
Disponible sur les systèmes NT 32 et 64bits Vista et supérieur. |
Prompt |
Renvoie la chaîne configurée pour l'affichage standard, par défaut : $P$G. Voir l'aide de la commande prompt pour plus d'information : prompt /?. |
Disponible sur tous les systèmes NT. |
PSModulePath |
Renvoie le chemin d'accès complet vers les modules PowerShell. |
Disponible uniquement sur les systèmes Vista et supérieur. (XP avec mise à jour KB926140) |
Public |
Renvoie le chemin d'accès complet vers le répertoire des documents publics sous la forme %HomeDrive%\Users\Public. |
Disponible uniquement sur les systèmes Vista et supérieur. |
Random |
Renvoie un nombre aléatoire compris entre 0 et 32767. |
Disponible sur tous les systèmes NT. |
SessionName |
Renvoie le nom de la session en cours. Par défaut : Console. |
Disponible sur tous les systèmes NT. |
SystemDrive |
Renvoie le point de montage de la partition sur laquelle est installé le système d'exploitation. |
Disponible sur tous les systèmes NT. |
SystemRoot |
Renvoie le chemin d'accès complet vers le système d'exploitation sous la forme |
Disponible sur tous les systèmes NT. |
Temp |
Renvoie le chemin d'accès complet jusqu'au répertoire des fichiers temporaires de l'utilisateur. Par défaut : %UserProfile%\AppData\Local\Temp. |
Disponible sur tous les systèmes NT. |
Time |
Renvoie l'heure en cours en utilisant le même format que la commande time. |
Disponible sur tous les systèmes NT. |
Tmp |
Identique à Temp. |
Disponible sur tous les systèmes NT. |
UserDomain |
Renvoie le nom de domaine de l'ordinateur sur lequel s'exécute le script. Si le système n'appartient pas à un domaine alors le nom de domaine sera le nom de l'ordinateur sur lequel s'exécute le script. |
Disponible sur tous les systèmes NT. |
UserName |
Renvoie le nom de l'utilisateur actuellement logué. |
Disponible sur tous les systèmes NT. |
UserProfile |
Renvoie le chemin d'accès complet vers le répertoire utilisateur de l'utilisateur actuellement logué sous la forme : %HomeDrive%\Users\%UserName%. |
Disponible sur tous les systèmes NT. |
WinDir |
Renvoie le chemin d'accès complet jusqu'au répertoire d'installation du système. Par défaut : %SystemDrive%\Windows. |
Disponible sur tous les systèmes NT. |
Tableau 3 : les variables d'environnement
II-B. La commande « set »▲
La commande set gère les variables dans l'interpréteur, elle permet :
- de créer une variable ;
- d'attribuer une valeur à une variable ;
- de modifier le contenu d'une variable ;
- de supprimer le contenu d'une variable ;
- d'effectuer des opérations mathématiques ou logiques entre des nombres ;
- de récupérer la saisie d'un utilisateur pour la placer dans une variable ;
- et de transtyper le contenu d'une variable.
Voici la syntaxe de la commande set.
Attribue à la variable une valeur sous forme de chaîne de caractères (où <variable> est son identifiant et <chaîne> est une chaîne de caractères).
set
["]<
variable>=<
chaîne>
["]
Attribue à la variable une valeur sous forme d'un nombre entier signé (où <expression> est une expression numérique à évaluer : détaillé par la suite).
set
/a ["]<
expression>
["]
Attribue à la variable une valeur, saisie par l'utilisateur, sous forme de chaîne de caractères après l'affichage de la chaîne d'invite si elle est spécifiée (où <variable> est son identifiant et où <chaîne_invite> est une chaîne de caractères affichée à l'utilisateur afin de l'inviter à saisir une chaîne au clavier).
set
/p ["]<
variable>=
[<
chaîne_invite>
]["]
Supprime la valeur de la variable de la mémoire, son identifiant reste indexé par l'interpréteur, mais sa valeur est indéfinie.
set
["]<
variable>=
["]
Voici les règles usuelles de la commande set :
- Si la commande set est employée sans paramètre, elle affiche les variables définies dans le contexte courant (détaillé par la suite).
- Si elle est utilisée avec comme paramètre une chaîne (ou un nom de variable), sans valeur ni signe égal ; alors elle affiche la variable dont le nom correspond à la chaîne donnée en paramètre et/ou les variables ayant un nom commençant par la chaîne donnée en paramètre.
- Si elle est utilisée avec un nom de variable et un signe égal sans valeur alors le contenu de la variable est vidé de la mémoire, il est possible de tester si une variable est définie, mais nous aborderons ce point au prochain chapitre.
- Toute chaîne non numérique dans l'expression à évaluer est traitée comme un identifiant de variable et est convertie en nombre avant d'être utilisée (dans son utilisation avec le paramètre /a), si la variable n'existe pas ou qu'elle est indéfinie, elle prend comme valeur 0.
- Une expression numérique à évaluer devrait toujours être placée entre guillemets afin de permettre l'utilisation des opérateurs logiques et des opérateurs de groupement.
Voyons plus en détail le fonctionnement de la commande set avec le script 9.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
@
echo
off
set
VAR_Espace_Un=
"Ma chaîne avec des espaces"
set
VAR_Espace_Deux"=
Ma chaîne avec des espaces"
set
"VAR_Espace_Trois=
Ma chaîne avec des espaces"
::il y a quatre espaces à la fin de la ligne suivante
set
/p VAR_Saisie_Un=
ma saisie un:
set
/p "VAR_Saisie_Deux=
ma saisie deux: "
set
/a VAR_Calcul_Un=
1+10
set
/a VAR_Calcul_Deux"=
2+20"
set
/a "VAR_Calcul_Trois=
3+30"
echo.
echo
%VAR_Espace_Un%
echo
%VAR_Espace_Deux%
echo
%VAR_Espace_Trois%
echo.
set
VAR_Saisie
echo.
set
VAR_Calcul
echo.
pause
Comme vous pouvez le constater, les guillemets occupent une place prépondérante dans ce script ; observez bien où ils sont placés. De la ligne 2 à la ligne 4, les valeurs des variables sont des chaînes de caractères avec des espaces. La déclaration de la variable VAR_Espace_Un se fait avec des guillemets placés de part et d'autre de la chaîne, la déclaration de la variable VAR_Espace_Deux se fait avec des guillemets : un placé avant le signe égal et l'autre à la fin de la chaîne ; la déclaration de la troisième variable VAR_Espace_Trois se fait avec des guillemets : un placé avant le nom de la variable et l'autre à la fin de la ligne. Si on se reporte à l'affichage obtenu, on remarque que la première et la troisième variables affichent une sortie correcte ; la deuxième variable quant à elle ne fonctionne pas ; on déduit donc facilement que si les guillemets s'ouvrent avant l'identifiant de la variable et se ferment après la valeur de la variable, ils ne font pas partie de la variable ; et si les guillemets s'ouvrent avant la valeur de la variable et se ferment après la valeur de la variable, ils font partie de la variable.
Les deux saisies de l'utilisateur donnent aussi un résultat intéressant, la ligne de commande qui déclare VAR_Saisie_Un n'utilise pas de guillemets ; par contre la ligne de commande qui déclare VAR_Saisie_Deux, elle, en utilise. Lors de l'affichage des invites de saisie utilisateur, les espaces se trouvant avant les chaînes d'invites ont disparu, et ceux se trouvant après sont affichés. On en déduit que les espaces en début de ligne sont ignorés et que ceux de la fin sont considérés comme faisant partie de la chaîne d'invite.
En ce qui concerne les variables VAR_Calcul_XXX, elles donnent toutes un résultat valide cependant il est toujours préférable de respecter les règles de syntaxe ; c'est-à-dire toujours mettre l'expression numérique entre guillemets. Cela sera utile dans la suite du chapitre.
II-C. Les variables de type chaîne de caractères▲
Ce sont, à mon sens, les plus utilisées en batch. Ce sont toutes les variables composées de caractères alphanumériques et autres caractères. Les chaînes de caractères sont déclarées via la commande set et il n'est pas nécessaire d'utiliser des guillemets pour que les espaces soient pris en compte comme faisant partie de la chaîne, et ce, où qu'ils soient placés dans la chaîne.
Elles offrent aussi d'autres possibilités comme la substitution de chaîne dans l'expansion de la variable. Ainsi avec la syntaxe suivante, il est possible de modifier la variable lors de son expansion sans que cela n'altère sa valeur (où <variable> est l'identifiant de la variable, <old_string> est la chaîne à remplacer et <new_string> est la chaîne à insérer à la place de <old_string>). Exemple avec le script 10.
%<variable>:<old_string>=<new_string>%
2.
3.
4.
5.
6.
@
echo
off
set
"X=
une chaîne"
echo
La variable avec substitution: %X:une=ma%
echo
La valeur réelle de la variable: %X%
echo.
pause
<old_string> peut aussi être une chaîne précédée d'un astérisque (*), dans ce cas, la substitution commencera du début de la valeur de la variable et se finira après la première occurrence de la chaîne spécifiée dans la substitution. Exemple avec le script 11.
2.
3.
4.
5.
@
echo
off
set
"X=
string"
echo
Substitution avec astérisque: %X:*t=d%
echo.
pause
Il ne faut jamais utiliser un astérisque seul dans la substitution, sans quoi, l'interpréteur de commande se fermera sans autres formes de procès. L'astérisque en fin de chaîne ne provoque pas d'erreur, mais ne permet pas la substitution, la variable est expansée avec sa valeur d'origine.
La substitution ne modifie pas la valeur de la variable, elle transforme juste la variable au moment de son expansion. Pour modifier la valeur de la variable, il faut le faire avec la commande set. Exemple avec le script 12.
2.
3.
4.
5.
6.
@
echo
off
set
"VAR=
ma chaîne"
echo
%VAR%
set
"VAR=
%VAR:chaîne=voiture%
"
echo
%VAR%
pause
Vous pouvez aussi ne développer qu'une partie d'une chaîne en sélectionnant les caractères voulus. Cela se fait via la syntaxe suivante (où <variable> est l'identifiant de la variable, <index> est l'index du premier caractère en partant de 0 et <longueur> est la longueur de la sélection). Exemple avec le script 13.
%<variable>:~<index>,<longueur>%
2.
3.
4.
5.
6.
@
echo
off
set
"VAR=
ma chaîne"
echo
%VAR:~0,2%
set
"VAR=
%VAR:~3,6%
"
echo
%VAR%
pause
Si la <longueur> n'est pas spécifiée, tout le reste de la chaîne est sélectionné, en partant du caractère fourni en <index>. Si une des valeurs données est négative, alors la valeur utilisée est la longueur totale de la chaîne ajoutée à la valeur négative.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
@
echo
off
rem Dans ce script, les caractères ^^ servent à échapper les caractères spéciaux.
set
"VAR=
ma chaîne"
rem longueur de var : 9 caractères
echo
%VAR:~-6%
rem 9+^(-6^) = 9-6 = 3
rem la chaîne sera développée à partir de l'index 3 et jusqu'à la fin ^(aucune longueur n'est donnée^).
echo
%VAR:~0,-7%
rem 9+^(-7^) = 9-7 = 2
rem la chaîne sera développée à partir de l'index 0 et sur une longueur de 2 caractères
pause
II-D. Les variables de type nombre entier signé▲
La commande set /a permet d'utiliser les nombres entiers signés allant de -2 147 483 648 à +2 147 483 647 (codés sur 32bits). Si vous n'utilisez pas le paramètre /a dans la commande, alors la séquence de chiffres sera considérée comme étant une chaîne de caractères, et non un nombre. La syntaxe de la commande set /a est la suivante.
set
/a <
expression>
<expression> utilise, quant à elle, la syntaxe suivante.
["]<
identifiant>
[<
attribution>
[{<
immédiate>|<
sous-expression>
}]]["]
Les expressions numériques doivent être placées entre des guillemets si elles contiennent des opérateurs logiques ou de groupement (détaillé plus loin dans ce chapitre). Ces différentes constituantes sont :
- <identifiant> qui est l'identifiant de la variable de destination ;
- <attribution> qui est l'opérateur d'attribution de l'expression, comme le signe égal qui effectue l'opération d'attribuer une valeur à une variable, d'autres opérateurs d'attribution seront détaillés dans cette section ;
- puis soit <immédiate> qui est une valeur immédiate (un nombre quelconque), soit <sous-expression> qui est une sous-expression, ces dernières seront détaillées dans le reste de cette section.
Vous pouvez spécifier plusieurs expressions en les séparant par des virgules.
@
echo
off
set
/a "VAR1=
1", "VAR2=
2"
echo
VAR1: %VAR1%
echo
VAR2: %VAR2%
pause
Les chaînes de caractères présentes dans l'expression sont considérées comme des variables et sont expansées (à l'exception de l'identifiant de la variable de destination), cela permet de faire des opérations sur des variables sans avoir à utiliser de symbole % pour leur expansion.
@
echo
off
set
/a "VAR1=
1"
set
/a "VAR2=
VAR1"
echo
VAR1: %VAR1%
echo
VAR2: %VAR2%
pause
Si un nom de variable inexistante ou indéfinie est utilisé, alors elle prend la valeur 0.
2.
3.
4.
5.
@
echo
off
rem La variable VAR1 n'existe pas.
set
/a "VAR2=
VAR1"
echo
VAR2: %VAR2%
pause
Les sous-expressions sont constituées de nombres, d'opérateurs et éventuellement de parenthèses, ces dernières s'utilisent de la même manière qu'en mathématiques et elles n'ont pas besoin d'être échappées si, et seulement si, les guillemets sont utilisés. Toutes les constituantes d'une sous-expression sont soumises à la préséance, c'est-à-dire l'ordre dans lequel les diverses constituantes vont être évaluées. L'ordre de préséance utilisé est le suivant (dans l'ordre décroissant d'évaluation) :
- le groupement soit ( ) ;
- les opérateurs unaires ! ~ - ;
- les opérateurs arithmétiques * / % ;
- les opérateurs arithmétiques + - ;
- le décalage logique << >> ;
- le ET au niveau du bit & ;
- le OU exclusif au niveau du bit ^ ;
- le OU inclusif au niveau du bit | ;
- l''attribution = *= /= %= += -= &= ^= |= <<= >>= ;
- le séparateur d'expression ,.
Il faut également noter que l'utilisation du nombre -2 147 483 648 tel quel dans une expression provoque une erreur, c'est dû au transtypage effectué par l'interpréteur. Ce dernier évalue d'abord la chaîne 2 147 483 648 afin de la transtyper puis lui applique l'opérateur unaire -. Mais comme le nombre 2 147 483 648 va au-delà de la définition d'un nombre sur 32bits, l'opération génère une erreur, exemple avec le script suivant.
2.
3.
4.
5.
6.
7.
8.
9.
10.
@
echo
off
echo
Nombre brute:
set
/a "ParseError=
-2147483648"
echo
%ParseError%
echo.
echo
Nombre avec évaluation:
set
/a "ParseError=
-2147483647 - 1"
echo
%ParseError%
echo.
pause
Une autre méthode qui fonctionne pour ce cas est la suivante.
2.
3.
4.
5.
@
echo
off
set
"SInt32=
-2147483648"
set
/a "SInt32"
echo
%SInt32%
pause
II-D-1. Les opérations arithmétiques▲
La commande set /a prend en charge les cinq opérations arithmétiques suivantes :
- l'addition ;
- la soustraction ;
- la multiplication ;
- la division ;
- et le modulo (le modulo est une opération qui sert à récupérer le reste d'une division).
Opérateur arithmétique |
Opérateur d'attribution |
Opération effectuée |
+ |
+= |
Addition |
- |
-= |
Soustraction |
* |
*= |
Multiplication |
/ |
/= |
Division |
% |
%= |
Modulo |
Tableau 4 : Opérations arithmétiques
Une note particulière pour le modulo : dans un script de commande, le symbole du modulo % doit être préfixé du caractère d'échappement %. Dans ce cas, c'est un piège dans lequel le novice peut se perdre, le premier symbole % est un caractère d'échappement qui permet au deuxième symbole %, le modulo, d'être pris en compte à l'exécution du script. Dans l'interpréteur de commande, le modulo (%) n'as pas besoin d'être échappé, c'est une spécificité du script. Ainsi la commande suivante fonctionne dans l'interpréteur.
set
/a "Mod=
5 % 2"
Alors que celle-ci ne fonctionne pas.
set
/a "Mod=
5 %% 2"
Dans un script, par contre, c'est l'inverse.
2.
3.
4.
5.
6.
@
echo
off
set
/a "Mod=
5 % 2"
echo
Mod1: %Mod%
set
/a "Mod=
5 %% 2"
echo
Mod2: %Mod%
pause
Ainsi, les opérations arithmétiques s'utilisent de manière « classique » dans les sous-expressions. Si une variable inexistante ou indéfinie est utilisée dans une sous-expression, elle prend la valeur 0, exemple avec le script 21.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
@
echo
off
set
/a "Un=
1"
set
/a "Deux=
1 + 1"
set
/a "Trois=
4 - 1"
set
/a "Quatre=
2 *
2"
set
/a "Cinq=
10 / 2"
set
/a "Six=
26 %% 10"
set
/a "Sept=
(( 2 + 1 ) *
2 ) + 1"
rem La variable Dix n'existe pas.
set
/a "Huit=
8+Dix","Neuf=
10-Un"
echo
%Un%
, %Deux%
, %Trois%
, %Quatre%
, %Cinq%
, %Six%
, %Sept%
, %Huit%
, %Neuf%
echo.
pause
L'attribution permet d'effectuer des opérations sur des variables existantes, si une variable inexistante ou indéfinie est utilisée, elle prend la valeur 0.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
@
echo
off
set
/a "Attrib1=
1","Attrib2=
2","Attrib3=
3","Attrib4=
4","Attrib5=
5"
rem La variable Attrib0 n'existe pas.
set
/a "Attrib0+=
1"
set
/a "Attrib1+=
1"
set
/a "Attrib2*=
2"
set
/a "Attrib3-=
2"
set
/a "Attrib4/=
2"
set
/a "Attrib5%%=
2"
echo
%Attrib0%
, %Attrib1%
, %Attrib2%
, %Attrib3%
, %Attrib4%
, %Attrib5%
echo.
pause
II-D-2. Les opérations logiques▲
Elles ne peuvent s'effectuer que sur des nombres et suivent les règles de l'algèbre de Boole. Les opérations logiques prises en charge par la commande set /a possèdent, elles aussi, des opérateurs d'attribution. Exemple avec le script 23.
Opérateur logique |
Opérateur d'attribution |
Description |
& |
&= |
ET logique au niveau du bit. (Bitwise AND) |
^ |
^= |
OU exclusif au niveau du bit. (Bitwise XOR) |
| |
|= |
OU inclusif au niveau du bit. (Bitwise OR) |
<< |
<<= |
Décalage logique vers la gauche. Le bit entrant est à zéro (pour l'élévation par puissances de deux). |
>> |
>>= |
Décalage logique vers la droite. Le bit entrant est égal au bit de signe (pour la propagation du bit de signe). |
Tableau 5 : Les opérateurs logiques
@
echo
off
set
/a "Val1=
-2147483647 - 1"
set
/a "Val2=
1"
set
/a "Val1SwitchL01=
Val1<<
1","Val1SwitchL15=
Val1<<
15","Val1SwitchL31=
Val1<<
31"
set
/a "Val2SwitchL01=
Val2<<
1","Val2SwitchL15=
Val2<<
15","Val2SwitchL31=
Val2<<
31"
set
/a "Val1SwitchR01=
Val1>>
1","Val1SwitchR15=
Val1>>
15","Val1SwitchR31=
Val1>>
31"
set
/a "Val2SwitchR01=
Val2>>
1","Val2SwitchR15=
Val2>>
15","Val2SwitchR31=
Val2>>
31"
set
/a "Val1AttribSwitchR=
Val1","Val1AttribSwitchR>>=
15"
set
/a "Val2AttribSwitchL=
Val2","Val2AttribSwitchL<<=
15"
echo
%Val1%
^<
^<
1: %Val1SwitchL01%
echo
%Val1%
^<
^<
15: %Val1SwitchL15%
echo
%Val1%
^<
^<
31: %Val1SwitchL31%
echo.
echo
%Val2%
^<
^<
1: %Val2SwitchL01%
echo
%Val2%
^<
^<
15: %Val2SwitchL15%
echo
%Val2%
^<
^<
31: %Val2SwitchL31%
echo.
echo
%Val1%
^>
^>
1: %Val1SwitchR01%
echo
%Val1%
^>
^>
15: %Val1SwitchR15%
echo
%Val1%
^>
^>
31: %Val1SwitchR31%
echo.
echo
%Val2%
^>
^>
1: %Val2SwitchR01%
echo
%Val2%
^>
^>
15: %Val2SwitchR15%
echo
%Val2%
^>
^>
31: %Val2SwitchR31%
echo.
echo
%Val1%
^>
^>=
15: %Val1AttribSwitchR%
echo
%Val2%
^<
^<=
15: %Val2AttribSwitchL%
echo.
set
/a "Val3=
1431655765"
set
/a "Val4=
-858993460"
set
/a "Val3LogicalOrVal4=
Val3 |
Val4"
set
/a "Val3LogicalXorVal4=
Val3 ^ Val4"
set
/a "Val3LogicalAndVal4=
Val3 &
Val4"
set
/a "Val3AttribOrVal4=
Val3","Val3AttribOrVal4|=
Val4"
set
/a "Val3AttribXorVal4=
Val3","Val3AttribXorVal4^=
Val4"
set
/a "Val3AttribAndVal4=
Val3","Val3AttribAndVal4&=
Val4"
echo
%Val3%
^|
%Val4%
:
%Val3LogicalOrVal4%
echo
%Val3%
^|=
%Val4%
:
%Val3AttribOrVal4%
echo.
echo
%Val3%
^^ %Val4%
:
%Val3LogicalXorVal4%
echo
%Val3%
^^=
%Val4%
:
%Val3AttribXorVal4%
echo.
echo
%Val3%
^&
%Val4%
:
%Val3LogicalAndVal4%
echo
%Val3%
^&=
%Val4%
:
%Val3AttribAndVal4%
echo.
pause
II-D-3. Les opérateurs unaires▲
N.B. : Dans les sections II.D.3, II.ELes nombres entiers signés en notation hexadécimale et II.FLes nombres entiers signés en notation octale de ce document, sont abordés différents concepts de représentation numérique communément utilisés en informatique, tels que :
- l'écriture de nombre en binaire ;
- l'écriture de nombre en hexadécimal ;
- l'écriture de nombre en octal ;
- la représentation des nombres en complément à un ;
- la représentation des nombres entiers signés en complément à deux.
Il est donc nécessaire, si vous ne les connaissez pas, de faire des recherches sur Wikipédia.
Les opérateurs unaires ne s'appliquent qu'aux nombres qu'ils précèdent, ils ne possèdent donc pas d'opérateur d'attribution. Leur syntaxe est la suivante (où <unaire> est l'opérateur unaire et <nombre> est le nombre auquel il s'applique).
<
unaire><
nombre>
Opérateur unaire |
Description |
! |
NON logique, renvoie 1 si le nombre est égal à 0 et 0 sinon. (Logical NOT). |
~ |
NON au niveau du bit, complément à un. (Bitwise NOT) |
- |
Renvoie la valeur inverse, soit un nombre négatif si le nombre d'origine est positif, complément à deux. (NEG) |
Tableau 6 : Les opérateurs unaires
Exemple avec le script 24.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
@
echo
off
rem Veuillez vous assurer d'utiliser le bon encodage, sans quoi l'affichage ne sera pas celui attendu.
set
/a "Val1=
2147483647"
set
/a "Val2=
1"
set
/a "Val3=
0"
set
/a "LogicalNOT_Val1=
!Val1"
set
/a "LogicalNOT_Val2=
!Val2"
set
/a "LogicalNOT_Val3=
!Val3"
set
/a "BitwiseNOT_Val1=
~Val1"
set
/a "BitwiseNOT_Val2=
~Val2"
set
/a "BitwiseNOT_Val3=
~Val3"
set
/a "NEG_Val1=
-Val1"
set
/a "NEG_Val2=
-Val2"
set
/a "NEG_Val3=
-Val3"
echo
╔════════════════════════════════════════╗
echo
║ Nombre ║
echo
╔════════════╬═════════════╦═════════════╦════════════╣
echo
║ Opérateur ║ %Val1%
║ %Val2%
║ %Val3%
║
echo
╠════════════╬═════════════╬═════════════╬════════════╣
echo
║ LogicalNOT ║ %LogicalNOT_Val1%
║ %LogicalNOT_Val2%
║ %LogicalNOT_Val3%
║
echo
╠════════════╬═════════════╬═════════════╬════════════╣
echo
║ BitwiseNOT ║ %BitwiseNOT_Val1%
║ %BitwiseNOT_Val2%
║ %BitwiseNOT_Val3%
║
echo
╠════════════╬═════════════╬═════════════╬════════════╣
echo
║ NEG ║ %NEG_Val1%
║ %NEG_Val2%
║ %NEG_Val3%
║
echo
╚════════════╩═════════════╩═════════════╩════════════╝
echo.
pause
II-E. Les nombres entiers signés en notation hexadécimale▲
Les nombres en notation hexadécimale doivent être déclarés comme des nombres entiers en notation décimale (ce que nous avons utilisé jusqu'à maintenant) avec la commande set /a "<expression>" et toujours être préfixés par 0x (chiffre zéro suivi de la lettre « x »). La commande set /a a pour effet de transformer toutes les valeurs entrées en valeurs numériques. Si c'est une chaîne de caractères alors la commande cherchera une variable portant ce nom, si c'est un nombre hexadécimal alors la commande set /a codera le nombre tel quel en binaire. Cependant, même si un nombre est fourni en notation hexadécimale, l'interpréteur l'expanse toujours en notation décimale, exemple avec le script 25.
2.
3.
4.
5.
@
echo
off
rem le nombre 0x1 vaut 1 et le nombre 0x3 vaut 3.
set
/a "Hexa=
0x1 + 0x3"
echo
Résultat: %Hexa%
pause
Le résultat du script 25 nous montre que le calcul s'effectue correctement, de même avec le script 26.
2.
3.
4.
5.
6.
@
echo
off
rem le nombre 0x5 vaut 5 et le nombre 0x8 vaut 8.
set
/a "Hexa=
0x5 + 0x8"
rem Le résultat doit donc être 0xD qui vaut 13.
echo
Résultat: %Hexa%
pause
Jusque-là tout va bien même si le résultat n'est pas en notation hexadécimale, il est quand même celui attendu. Le problème c'est que l'interpréteur de commande utilise toujours la représentation en complément à deux pour coder un nombre entier signé. Ainsi, dès lors que l'on utilise un nombre en notation hexadécimale supérieur à 0x7FFFFFFF (soit 2 147 483 647 en notation décimale), ce nombre est en fait un nombre négatif comme le montre le script 27.
2.
3.
4.
@
echo
off
set
/a "Hexa=
0x80000000"
echo
Résultat: %Hexa%
pause
Un résultat plutôt déroutant pour quiconque ne s'y attend pas, en effet la représentation en complément à deux code les nombres négatifs de 0xFFFFFFFF, soit -1, à 0x80000000, soit -2 147 483 648. Exemple avec le script 28.
2.
3.
4.
5.
6.
@
echo
off
rem 0x80000000 vaut -2147483648 et 0x7FFFFFFF vaut 2147483647
set
/a "Hexa=
0x80000000 + 0x7FFFFFFF"
rem Le résultat doit donc être 0xFFFFFFFF qui vaut -1.
echo
Résultat: %Hexa%
pause
Comme vous pouvez le voir, on additionne 0x80000000 (soit -2 147 483 648 en représentation hexadécimale en complément à deux) à 0x7FFFFFFF (soit +2 147 483 647, toujours dans la même représentation) ce qui nous donne le résultat de -1, le résultat est un nombre entier signé décimal utilisant la représentation en complément à deux codé sur 32bits soit 0xFFFFFFFF. Pour récupérer un nombre hexadécimal, il faut le calculer à l'aide d'un algorithme de divisions successives ; et pour les nombres négatifs, de l'opérateur unaire « Bitwise NOT » dont dispose la commande set /a, voir le script 29.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
@
echo
off
rem On récupère un nombre sous forme de chaîne et le place dans SInt32.
set
"SInt32=
-2147483648"
rem On transtype SInt32 en nombre pour qu'il soit évalué correctement.
set
/a "SInt32"
rem Puis on effectue un NON au niveau du bit pour obtenir son complément à un ^(nombre négatif^).
set
/a "SInt32=
~SInt32"
rem On utilise un algorithme de divisions successives par 16 pour obtenir des
rem nombres en base 16 ^(hexadécimale^), chaque nombre obtenu forme un quartet.
set
/a "Nibble0=
SInt32 %% 16","HighOrder0=
SInt32 / 16"
set
/a "Nibble1=
HighOrder0 %% 16","HighOrder1=
HighOrder0 / 16"
set
/a "Nibble2=
HighOrder1 %% 16","HighOrder2=
HighOrder1 / 16"
set
/a "Nibble3=
HighOrder2 %% 16","HighOrder3=
HighOrder2 / 16"
set
/a "Nibble4=
HighOrder3 %% 16","HighOrder4=
HighOrder3 / 16"
set
/a "Nibble5=
HighOrder4 %% 16","HighOrder5=
HighOrder4 / 16"
set
/a "Nibble6=
HighOrder5 %% 16"
set
/a "Nibble7=
HighOrder5 / 16"
rem On retranche chaque valeur à 15 pour récupérer le nombre original.
set
/a "Nibble0=
15 - Nibble0"
set
/a "Nibble1=
15 - Nibble1"
set
/a "Nibble2=
15 - Nibble2"
set
/a "Nibble3=
15 - Nibble3"
set
/a "Nibble4=
15 - Nibble4"
set
/a "Nibble5=
15 - Nibble5"
set
/a "Nibble6=
15 - Nibble6"
set
/a "Nibble7=
15 - Nibble7"
rem Les quartets 0 à 3 forment le Least Significant Word ^(LSW^) ou mot de poids faible dans la langue de Molière.
set
"LSW=
%Nibble3%
%Nibble2%
%Nibble1%
%Nibble0%
"
rem Les quartets 4 à 7 forment le Most Significant Word ^(MSW^) ou mot de poids fort toujours dans la langue de Molière.
set
"MSW=
%Nibble7%
%Nibble6%
%Nibble5%
%Nibble4%
"
echo
%MSW%
%LSW%
rem On récupère un nouveau nombre ^(nombre positif^).
set
"SInt32=
2147483647"
set
/a "SInt32"
set
/a "Nibble0=
SInt32 %% 16","HighOrder0=
SInt32 / 16"
set
/a "Nibble1=
HighOrder0 %% 16","HighOrder1=
HighOrder0 / 16"
set
/a "Nibble2=
HighOrder1 %% 16","HighOrder2=
HighOrder1 / 16"
set
/a "Nibble3=
HighOrder2 %% 16","HighOrder3=
HighOrder2 / 16"
set
/a "Nibble4=
HighOrder3 %% 16","HighOrder4=
HighOrder3 / 16"
set
/a "Nibble5=
HighOrder4 %% 16","HighOrder5=
HighOrder4 / 16"
set
/a "Nibble6=
HighOrder5 %% 16"
set
/a "Nibble7=
HighOrder5 / 16"
set
"LSW=
%Nibble3%
%Nibble2%
%Nibble1%
%Nibble0%
"
set
"MSW=
%Nibble7%
%Nibble6%
%Nibble5%
%Nibble4%
"
echo
%MSW%
%LSW%
pause
La sortie du script 29 nous donne bien des nombres hexadécimaux, mais dans une représentation décimale (8 0 0 0 0 0 0 0 soit -2 147 483 648 codé 0x80000000 et 7 15 15 15 15 15 15 15 soit 2 147 483 647 codé 0x7FFFFFFF), pour pouvoir les convertir dans une notation hexadécimale ; il nous faut utiliser les conditions que nous étudierons au prochain chapitre.
II-F. Les nombres entiers signés en notation octale▲
Les nombres en notation octale fonctionnent comme les autres et supportent les mêmes opérations. Pour être considérés par l'interpréteur comme des nombres en notation octale, ils doivent être préfixés par 0 (chiffre zéro). Ils vont donc de -2 147 483 648 à 2 147 483 647 (codés sur 32bits) soit en notation octale -020000000000 à +017777777777. Les nombres en notation octale sont codés tels quels en binaire, mais le principe selon lequel tout nombre est expansé en entier signé en notation décimale codé en complément à deux s'applique aussi pour eux. Notez aussi qu'ils ont le même problème de transtypage que les nombres décimaux, exemple avec le script 30.
@
echo
off
set
/a "Octal1=
-017777777777 - 1"
set
/a "Octal2=
017777777777"
set
/a "Octal3=
Octal1 + Octal2"
echo
Octal1: %Octal1%
echo
Octal2: %Octal2%
echo
Octal3: %Octal3%
pause
Notez aussi que l'utilisation des nombres « 08 » et « 09 » génère toujours une erreur du fait qu'ils sont préfixés par « 0 » ; et, que « 8 » et « 9 » ne sont pas des chiffres octaux. Lorsque la commande set /a rencontre un « 0 » en début de nombre, elle considère que tous les chiffres qui se trouvent après sont des octaux et lorsqu'elle rencontre « 8 » ou « 9 », le transtypage ne peut s'effectuer et la commande se termine sur une erreur. Exemple avec le script 31.
@
echo
off
echo
Nombre 08:
set
/a "Octal1=
08"
echo.
echo
Nombre 09:
set
/a "Octal1=
09"
echo.
pause
III. Les conditions▲
Les conditions sont supportées par la commande if. Elle permet d'effectuer des comparaisons de toutes sortes et d'exécuter des commandes en fonction du résultat de cette comparaison. else et else if sont, quant à eux, des paramètres de la commande if et ne peuvent être utilisés qu'à l'intérieur de la commande if.
III-A. If, else, else if, not et /i▲
La commande if se traduit simplement par « si » : « si » la condition est vraie, fais ceci ; elle peut être accompagnée par le paramètre else qui se traduit par « sinon » : « si » la condition est vraie, fais ceci, « sinon » fais cela. La syntaxe de la commande if est la suivante.
if
<
con
dition>
<
commande1>
[else
<
commande2>
]
Comme vous pouvez le voir sur la syntaxe de la commande if, if vient en premier suivi de sa <condition> et d'une commande (<commande1>), puis vient else et une commande (<commande2>), on peut le traduire par :
- si (if) la <condition> se vérifie alors la <commande1> s'exécute ;
- sinon (else) c'est la <commande2> qui s'exécute.
Le paramètre else ne peut être utilisé seul, il doit toujours être utilisé avec la commande if, et être sur la même ligne. Il y a deux règles importantes à garder en tête lorsqu'on utilise des conditions :
- la première est que l'on ne peut utiliser qu'un maximum de 2048 caractères (sous Windows XP et inférieur) ou un peu plus de 4096 caractères (sous Windows Vista et supérieur) par ligne de commande, limite très vite atteinte avec plusieurs conditions, plus leurs commandes et leurs paramètres ;
- et la deuxième est que les parenthèses sont prises en compte par l'interpréteur de commande comme des opérateurs de bloc, c'est pour cela qu'il faut les échapper lorsque l'on ne s'en sert pas à cette fin.
Les opérateurs de bloc permettent d'utiliser un bloc de commande en lieu et place d'une commande. Ainsi, chaque fois que la syntaxe autorise l'utilisation d'une commande, celle-ci peut être remplacée par un bloc de commandes.
Ainsi la syntaxe de la commande if peut être modifiée de la manière suivante.
if
<
con
dition>
(
<
commande1>
<
commande2>
) else
(
<
commande3>
<
commande4>
)
L'utilisation des parenthèses comme opérateurs de bloc permet d'exécuter plusieurs commandes, par condition vérifiée, au lieu d'une seule. La parenthèse ouvrante associée à une condition doit se trouver sur la même ligne que cette dernière ; si une autre condition est utilisée conjointement, alors elle doit se trouver sur la même ligne que la parenthèse fermante associée à la condition qui la précède.
La commande if autorise aussi le paramètre else if qui se traduit par « sinon si », ce dernier permet de poser une condition supplémentaire avant l'exécution éventuelle du else. La syntaxe serait la suivante.
if
<
con
dition1>
<
cmd1>
[else
if
<
con
dition2>
<
cmd2>
] [else
<
cmd3>
]
Les opérateurs de bloc fonctionnent de la même manière avec le paramètre else if en plus. Il faut aussi noter que le paramètre else if peut être utilisé autant de fois que l'on veut dans la commande if.
if
<
con
dition1>
(
<
commande1>
<
commande2>
) else
if
<
con
dition2>
(
<
commande3>
<
commande4>
) else
if
<
con
dition3>
(
<
commande5>
<
commande6>
) else
(
<
commande7>
<
commande8>
)
La syntaxe ci-dessus pourrait être traduite par :
- si (if) la <condition1> est vraie : exécuter <commande1> et <commande2> ;
- sinon si (else if) <condition2> est vraie : exécuter <commande3> et <commande4> ;
- sinon si (else if) <condition3> est vraie : exécuter <commande5> et <commande6> ;
- sinon (else) : exécuter <commande7> et <commande8>.
Les conditions associées à la commande if et au paramètre else if prennent le modificateur not qui permet d'exécuter une commande si la condition est fausse. Le modificateur not ne s'applique pas à toutes les conditions de la même structure de contrôle. Si le paramètre else if est utilisé conjointement, son comportement dépendra de la présence, ou non, du modificateur not à l'intérieur de la condition.
if
not
<
con
dition1>
<
cmd1>
else
if
<
con
dition2>
<
cmd2>
else
<
cmd3>
La commande ci-dessus se traduit par :
- si (if) la <condition1> est fausse : exécuter la <cmd1> ;
- sinon (else if), si la <condition2> est vraie : exécuter la <cmd2> ;
- sinon (else), si la <condition1> est vraie et que la <condition2> est fausse, exécuter la <cmd3>.
Les conditions associées à la commande if et au paramètre else if prennent le modificateur /i qui permet de ne pas tenir compte de la casse lorsque la condition traite des chaînes de caractères. Ce modificateur ne fonctionne que sur les conditions qui traitent des chaînes de caractères. Si le paramètre else if est utilisé conjointement, son comportement dépendra de la présence, ou non, du modificateur /i à l'intérieur de la condition.
if
/i <
con
dition1>
<
cmd1>
else
if
/i <
con
dition2>
<
cmd2>
else
<
cmd3>
Se traduit par :
- si (if) la <condition1> est vraie, sans tenir compte de la casse, exécuter la <cmd1> ;
- sinon si (else if) la <condition2> est vraie, sans tenir compte de la casse, exécuter la <cmd2> ;
- sinon (else), si la <condition1> est fausse sans tenir compte de la casse et que la <condition2> est fausse sans tenir compte de la casse, exécuter la <cmd3>.
Nous allons maintenant voir quelles sont les conditions supportées par la commande if et le paramètre else if.
III-B. La condition « exist <path> »▲
La condition exist <path> permet de tester si un chemin d'accès ou un fichier existe. Elle peut être utilisée avec le paramètre else if et le modificateur not. Le modificateur /i ne fonctionne pas sur cette condition. Sa syntaxe est la suivante, où <path> est le chemin d'accès ou le fichier à tester. Si le chemin d'accès contient des espaces, il doit être placé entre guillemets.
if
[not
] exist
["]<
path
>
["] <
commande>
Exemple avec le script 32.
2.
3.
@
echo
off
if
exist
"%cd%
" echo
%cd%
existe bien.
pause
III-C. La condition « defined <variable> »▲
La condition defined <variable> permet de tester si une variable a une valeur définie. Elle peut être utilisée avec le paramètre else if, et le modificateur not. Le modificateur /i ne fonctionne pas sur cette condition. Voir le script 33.
if
[not
] defined
<
variable>
<
commande>
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
@
echo
off
set
"Def1=
"
set
"Def2=
abcd"
if
defined
Def1 (
echo
Def1 est définie.
) else
(
echo
Def1 n'est pas définie.
)
if
defined
Def2 (
echo
Def2 est définie.
) else
(
echo
Def2 n'est pas définie.
)
if
not
defined
Def3 (
echo
Def3 n'est pas définie.
) else
(
echo
Def3 est définie.
)
pause
III-D. La condition « errorlevel <opérande> »▲
La condition errorlevel <opérande> permet de tester si le code d'erreur de la dernière commande exécutée est égal ou supérieur au nombre donné en <opérande>. Le code d'erreur est un nombre renvoyé par une commande pour donner des informations sur le déroulement de son exécution, il est aussi accessible via la variable ErrorLevel. En général, le code d'erreur est 0 si aucune erreur ne s'est produite ; et il est égal ou supérieur à 1 si une erreur s'est produite, chaque code d'erreur correspondant à une erreur précise. Le nom de variable ErrorLevel est un nom de variable réservé, vous pouvez la modifier en l'écrasant avec une nouvelle valeur, cependant, elle prendra la valeur donnée jusqu'à la fin du script. Attention toutefois, il arrive que des commandes ne modifient pas la variable ErrorLevel ou bien qu'elle lui donne toujours le code 0, ceci est souvent dû à de mauvaises pratiques de programmation, fiez-vous à des codes d'erreurs connues et ayant une valeur concrète, ne traitez pas les autres, pour ce faire, consultez le manuel ou l'aide de la commande concernée. Si le modificateur not est utilisé, la <commande> seras exécutée seulement si ErrorLevel est inférieur à l'<opérande>. Le modificateur /i n'est pas supporté par la condition errorlevel <opérande>.
if
[not
] errorlevel
<
opérande>
<
commande>
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
@
echo
off
rem La commande « setlocal » crée un environnement local pour ne pas générer d'erreur si le script est appelé depuis l'interpréteur.
setlocal
set
"TestError1=
Un petit test"
if
not
errorlevel
1 echo
Aucune erreur dans la première commande.
echo.
set
/a "TestError2=
09"
echo.
if
errorlevel
1 echo
Le code d'erreur des nombres invalides est %ErrorLevel%
.
pause
rem La commande « endlocal » supprime l'environnement local.
endlocal
III-E. La condition « cmdextversion <opérande> »▲
La condition cmdextversion <opérande> permet de tester si le numéro de version des extensions de commande est égal ou supérieur au nombre donné en <opérande>. Le numéro de version des extensions de commande est à prendre en compte, car selon la version des extensions, le traitement des commandes peut être modifié. Reportez-vous à l'aide concernant les commandes citées dans le tableau 7 pour plus de détails. Chaque commande modifiée l'est d'une manière qui lui est propre ; ainsi la commande assoc ne sera pas modifiée de la même manière que la commande start, tout simplement parce qu'elle n'exécute pas du tout la même opération.
DEL ou ERASE |
COLOR |
CD ou CHDIR |
MD ou MKDIR |
PROMPT |
PUSHD |
POPD |
SET |
SETLOCAL |
ENDLOCAL |
IF |
FOR |
CALL |
SHIFT |
GOTO |
START |
ASSOC |
FTYPE |
Tableau 7 : Commandes soumises aux extensions
La condition cmdextversion <opérande> est toujours fausse si les extensions de commande sont désactivées ; la première version des extensions est la version « 1 » et le numéro de version est incrémenté de 1 à chaque nouvelle version. Si le numéro de version des extensions est égal ou supérieur à la valeur donnée dans la condition cmdextversion <opérande> alors la condition est vraie ; les extensions de commande étant rétrocompatibles d'une version à l'autre. Si le modificateur not est utilisé, la condition est vraie si le numéro de version des extensions de commande est inférieur au nombre donné en <opérande>. La condition cmdextversion <opérande> ne prend pas le modificateur /i. Le tableau 8 donne la liste des révisions d'extensions de commande en fonction du système d'exploitation.
Numéro de version |
Compatibilité |
1 |
Windows 2000. |
2 |
Tous les systèmes NT XP et supérieur. |
Tableau 8 : Révisions des extensions de commande
if
[not
] cmdextversion
<
opérande>
<
commande>
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
@
echo
off
rem La commande « setlocal enableextensions » active les extensions de commande.
setlocal
enableextensions
if
not
cmdextversion
1 (
echo
CmdExtVersion
1: Not
Detected.
) else
(
echo
CmdExtVersion
1: Detected.
)
if
cmdextversion
2 (
echo
CmdExtVersion
2: Detected.
) else
(
echo
CmdExtVersion
2: Not
Detected.
)
if
cmdextversion
3 (
echo
CmdExtVersion
3: Detected.
) else
(
echo
CmdExtVersion
3: Not
Detected.
)
pause
III-F. La condition « <chaîne1> <comparateur> <chaîne2> »▲
La condition <chaîne1> <comparateur> <chaîne2> permet d'effectuer des comparaisons sur des chaînes et des nombres. La comparaison de chaînes est effectuée au moyen d'un XOR au niveau du bit entre chaque octet d'une chaîne puis les résultats obtenus sur les différentes chaînes sont comparés numériquement. Si les deux chaînes ne sont constituées que de chiffres alors elles sont transtypées en nombre puis évaluées numériquement. Les comparateurs, pris en compte par la condition <chaîne1> <comparateur> <chaîne2>, sont listés dans le tableau 9. La condition <chaîne1> <comparateur> <chaîne2> prend les modificateurs not et /i. Sa syntaxe est la suivante, si les chaînes contiennent des espaces, elles doivent être placées entre guillemets.
if
[/i] [not
] ["]<
chaîne1>
["] <
comparateur>
["]<
chaîne2>
["] <
cmd
>
Comparateur |
Description |
EQU |
« EQUal », la condition est vraie si les deux chaînes sont égales. |
NEQ |
« Not EQual », la condition est vraie si les deux chaînes sont différentes. |
LSS |
« LeSS », la condition est vraie si chaîne1 est inférieure à chaîne2. |
LEQ |
« Less or EQual », la condition est vraie si chaîne1 est inférieure ou égale à chaîne2. |
GTR |
« GreaTeR », la condition est vraie si chaîne1 est supérieure à chaîne2. |
GEQ |
« Greater or EQual », la condition est vraie si chaîne1 est supérieure ou égale à chaîne2. |
Tableau 9 : Opérateurs de comparaison
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
@
echo
off
cls
set
"Chaîne1=
Un Petit test"
rem Encodage de la Chaîne1 :55 6E 20 50 65 74 69 74 20 74 65 73 74
set
/a "C1X=
0x55^0x6E^0x20^0x50^0x65^0x74^0x69^0x74^0x20^0x74^0x65^0x73^0x74"
set
"Chaîne2=
Un petit test"
rem Encodage de la Chaîne2 : 55 6E 20 70 65 74 69 74 20 74 65 73 74
set
/a "C2X=
0x55^0x6E^0x20^0x70^0x65^0x74^0x69^0x74^0x20^0x74^0x65^0x73^0x74"
set
"Chaîne3=
Un grand test"
rem Encodage de la Chaîne3 : 55 6E 20 67 72 61 6E 64 20 74 65 73 74
set
/a "C3X=
0x55^0x6E^0x20^0x67^0x72^0x62^0x6E^0x64^0x20^0x74^0x65^0x73^0x74"
set
"Chaîne4=
41"
set
"Chaîne5=
12"
echo
Chaîne 1 Chaîne 2 Chaîne 3
echo
=============================================================
echo
Chaîne %Chaîne1%
%Chaîne2%
%Chaîne3%
echo
Évaluation %C1X%
%C2X%
%C3X%
echo.
if
"%Chaîne1%
" GTR
"%Chaîne2%
" (
echo
"%Chaîne1%
" est supérieur à "%Chaîne2%
".
) else
if
"%Chaîne1%
" EQU
"%Chaîne2%
" (
echo
"%Chaîne1%
" est égal à "%Chaîne2%
".
) else
if
"%Chaîne1%
" LSS
"%Chaîne2%
" (
echo
"%Chaîne1%
" est inférieur à "%Chaîne2%
".
)
if
/i "%Chaîne1%
" GTR
"%Chaîne2%
" (
echo
"%Chaîne1%
" est supérieur à "%Chaîne2%
".
) else
if
/i "%Chaîne1%
" EQU
"%Chaîne2%
" (
echo
"%Chaîne1%
" est égal à "%Chaîne2%
".
) else
if
/i "%Chaîne1%
" LSS
"%Chaîne2%
" (
echo
"%Chaîne1%
" est inférieur à "%Chaîne2%
".
)
if
"%Chaîne1%
" GTR
"%Chaîne3%
" (
echo
"%Chaîne1%
" est supérieur à "%Chaîne3%
".
) else
if
"%Chaîne1%
" EQU
"%Chaîne3%
" (
echo
"%Chaîne1%
" est égal à "%Chaîne3%
".
) else
if
"%Chaîne1%
" LSS
"%Chaîne3%
" (
echo
"%Chaîne1%
" est inférieur à "%Chaîne3%
".
)
if
/i "%Chaîne1%
" GTR
"%Chaîne3%
" (
echo
"%Chaîne1%
" est supérieur à "%Chaîne3%
".
) else
if
/i "%Chaîne1%
" EQU
"%Chaîne3%
" (
echo
"%Chaîne1%
" est égal à "%Chaîne3%
".
) else
if
/i "%Chaîne1%
" LSS
"%Chaîne3%
" (
echo
"%Chaîne1%
" est inférieur à "%Chaîne3%
".
)
if
"%Chaîne3%
" GTR
"%Chaîne2%
" (
echo
"%Chaîne3%
" est supérieur à "%Chaîne2%
".
) else
if
"%Chaîne3%
" EQU
"%Chaîne2%
" (
echo
"%Chaîne3%
" est égal à "%Chaîne2%
".
) else
if
"%Chaîne3%
" LSS
"%Chaîne2%
" (
echo
"%Chaîne3%
" est inférieur à "%Chaîne2%
".
)
if
/i "%Chaîne3%
" GTR
"%Chaîne2%
" (
echo
"%Chaîne3%
" est supérieur à "%Chaîne2%
".
) else
if
/i "%Chaîne3%
" EQU
"%Chaîne2%
" (
echo
"%Chaîne3%
" est égal à "%Chaîne2%
".
) else
if
/i "%Chaîne3%
" LSS
"%Chaîne2%
" (
echo
"%Chaîne3%
" est inférieur à "%Chaîne2%
".
)
echo.
echo.
echo
Chaîne 4 Chaîne 5
echo
=============================================================
echo
Chaîne %Chaîne4%
%Chaîne5%
echo.
if
%Chaîne4%
GTR
%Chaîne5%
(
echo
%Chaîne4%
est supérieur à %Chaîne5%
.
) else
if
%Chaîne4%
EQU
%Chaîne5%
(
echo
%Chaîne4%
est égal à %Chaîne5%
.
) else
if
%Chaîne4%
LSS
%Chaîne5%
(
echo
%Chaîne4%
est inférieur à %Chaîne5%
.
)
echo.
pause
III-G. La condition « "<chaîne1>"=="<chaîne2>" »▲
La condition "<chaîne1>"=="<chaîne2>" permet de tester une égalité entre des chaînes de caractères. Les chaînes doivent être placées entre guillemets si elles sont susceptibles de contenir des espaces. Cette condition prend les modificateurs not et /i.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
@
echo
off
cls
set
"Un=
Une Chaîne"
set
"Deux=
une chaîne"
if
"%Un%
"==
"%Deux%
" (
echo
If
: "%Un%
" est égale à "%Deux%
".
) else
(
echo
If
: "%Un%
" n'est pas égale à "%Deux%
".
)
if
not
"%Un%
"==
"%Deux%
" (
echo
If
Not
: "%Un%
" n'est pas égale à "%Deux%
".
) else
(
echo
If
Not
: "%Un%
" est égale à "%Deux%
".
)
if
/i "%Un%
"==
"%Deux%
" (
echo
If
/i: "%Un%
" est égale à "%Deux%
".
) else
(
echo
If
/i: "%Un%
" n'est pas égale à "%Deux%
".
)
pause
III-H. Mise en application de la commande « if »▲
Dans cette section, nous allons reprendre le script 29 qui permettait de transformer un entier en sa représentation hexadécimale en notation en complément à deux. Grâce à la commande if, nous allons faire en sorte de traiter les nombres négatifs et positifs avec la même fonction de calcul et d'afficher le résultat dans une forme hexadécimale correcte.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
@
echo
off
rem On crée un environnement local pour ne pas générer d'erreur.
setlocal
rem On récupère un nombre sous forme de chaîne et on le place dans SInt32.
set
/p "SInt32=
Entrez un nombre entre -2147483648 et 2147483647: "
rem On transtype SInt32 en nombre pour qu'il soit évalué correctement.
set
/a "SInt32"
rem On utilise une variable témoin pour savoir si le nombre est
rem négatif ou pas sans avoir à le réévaluer à chaque fois.
rem Par défaut, on considère que le nombre est positif.
set
"NegNum=
false"
rem Si SInt32 est inférieur à zéro, il est négatif,
rem alors on effectue un NON au niveau du bit pour obtenir son complément à un
rem et on met NegNum à "true" pour définir le nombre comme négatif.
if
%SInt32%
LSS
0 (
set
/a "SInt32=
~SInt32"
set
"NegNum=
true"
)
rem On utilise un algorithme de divisions successives par 16 pour obtenir des
rem nombres en base 16, chaque nombre obtenu forme un quartet.
set
/a "Nibble0=
SInt32 %% 16","HighOrder0=
SInt32 / 16"
set
/a "Nibble1=
HighOrder0 %% 16","HighOrder1=
HighOrder0 / 16"
set
/a "Nibble2=
HighOrder1 %% 16","HighOrder2=
HighOrder1 / 16"
set
/a "Nibble3=
HighOrder2 %% 16","HighOrder3=
HighOrder2 / 16"
set
/a "Nibble4=
HighOrder3 %% 16","HighOrder4=
HighOrder3 / 16"
set
/a "Nibble5=
HighOrder4 %% 16","HighOrder5=
HighOrder4 / 16"
set
/a "Nibble6=
HighOrder5 %% 16"
set
/a "Nibble7=
HighOrder5 / 16"
rem Si c'est un nombre négatif ^("%NegNum%"=="true"^),
rem on retranche chaque valeur à 15 pour récupérer le nombre original.
if
"%NegNum%
"==
"true" (
set
/a "Nibble0=
15 - Nibble0"
set
/a "Nibble1=
15 - Nibble1"
set
/a "Nibble2=
15 - Nibble2"
set
/a "Nibble3=
15 - Nibble3"
set
/a "Nibble4=
15 - Nibble4"
set
/a "Nibble5=
15 - Nibble5"
set
/a "Nibble6=
15 - Nibble6"
set
/a "Nibble7=
15 - Nibble7"
)
rem On transforme chaque quartet en notation hexadécimale.
if
%Nibble0%
GTR
9 (
if
%Nibble0%
EQU
10 set
"Nibble0=
A"
if
%Nibble0%
EQU
11 set
"Nibble0=
B"
if
%Nibble0%
EQU
12 set
"Nibble0=
C"
if
%Nibble0%
EQU
13 set
"Nibble0=
D"
if
%Nibble0%
EQU
14 set
"Nibble0=
E"
if
%Nibble0%
EQU
15 set
"Nibble0=
F"
)
if
%Nibble1%
GTR
9 (
if
%Nibble1%
EQU
10 set
"Nibble1=
A"
if
%Nibble1%
EQU
11 set
"Nibble1=
B"
if
%Nibble1%
EQU
12 set
"Nibble1=
C"
if
%Nibble1%
EQU
13 set
"Nibble1=
D"
if
%Nibble1%
EQU
14 set
"Nibble1=
E"
if
%Nibble1%
EQU
15 set
"Nibble1=
F"
)
if
%Nibble2%
GTR
9 (
if
%Nibble2%
EQU
10 set
"Nibble2=
A"
if
%Nibble2%
EQU
11 set
"Nibble2=
B"
if
%Nibble2%
EQU
12 set
"Nibble2=
C"
if
%Nibble2%
EQU
13 set
"Nibble2=
D"
if
%Nibble2%
EQU
14 set
"Nibble2=
E"
if
%Nibble2%
EQU
15 set
"Nibble2=
F"
)
if
%Nibble3%
GTR
9 (
if
%Nibble3%
EQU
10 set
"Nibble3=
A"
if
%Nibble3%
EQU
11 set
"Nibble3=
B"
if
%Nibble3%
EQU
12 set
"Nibble3=
C"
if
%Nibble3%
EQU
13 set
"Nibble3=
D"
if
%Nibble3%
EQU
14 set
"Nibble3=
E"
if
%Nibble3%
EQU
15 set
"Nibble3=
F"
)
if
%Nibble4%
GTR
9 (
if
%Nibble4%
EQU
10 set
"Nibble4=
A"
if
%Nibble4%
EQU
11 set
"Nibble4=
B"
if
%Nibble4%
EQU
12 set
"Nibble4=
C"
if
%Nibble4%
EQU
13 set
"Nibble4=
D"
if
%Nibble4%
EQU
14 set
"Nibble4=
E"
if
%Nibble4%
EQU
15 set
"Nibble4=
F"
)
if
%Nibble5%
GTR
9 (
if
%Nibble5%
EQU
10 set
"Nibble5=
A"
if
%Nibble5%
EQU
11 set
"Nibble5=
B"
if
%Nibble5%
EQU
12 set
"Nibble5=
C"
if
%Nibble5%
EQU
13 set
"Nibble5=
D"
if
%Nibble5%
EQU
14 set
"Nibble5=
E"
if
%Nibble5%
EQU
15 set
"Nibble5=
F"
)
if
%Nibble6%
GTR
9 (
if
%Nibble6%
EQU
10 set
"Nibble6=
A"
if
%Nibble6%
EQU
11 set
"Nibble6=
B"
if
%Nibble6%
EQU
12 set
"Nibble6=
C"
if
%Nibble6%
EQU
13 set
"Nibble6=
D"
if
%Nibble6%
EQU
14 set
"Nibble6=
E"
if
%Nibble6%
EQU
15 set
"Nibble6=
F"
)
if
%Nibble7%
GTR
9 (
if
%Nibble7%
EQU
10 set
"Nibble7=
A"
if
%Nibble7%
EQU
11 set
"Nibble7=
B"
if
%Nibble7%
EQU
12 set
"Nibble7=
C"
if
%Nibble7%
EQU
13 set
"Nibble7=
D"
if
%Nibble7%
EQU
14 set
"Nibble7=
E"
if
%Nibble7%
EQU
15 set
"Nibble7=
F"
)
rem Les quartets 0 à 3 forment le Least Significant Word.
set
"LSW=
%Nibble3%%Nibble2%%Nibble1%%Nibble0%
"
rem Les quartets 4 à 7 forment le Most Significant Word.
set
"MSW=
%Nibble7%%Nibble6%%Nibble5%%Nibble4%
"
rem on affiche le résultat.
echo
0x%MSW%%LSW%
rem Fin de l'environnement local.
endlocal
pause
Ce script sera un script témoin que nous ferons évoluer tout au long de ce document, il nous permettra de comparer les différentes approches et ce qu'elles impliquent comme contrainte d'utilisation.
En son état actuel, ce script est gourmand en ressource système, car à chaque ligne de commande correspond un appel vers l'interpréteur augmentant d'autant son temps de traitement. Les boucles for devraient nous permettre de réduire considérablement son coût en temps d'exécution et la taille du script par la même occasion.
IV. Tout dépend du contexte▲
Un script batch s'exécute toujours de manière linéaire, du début vers la fin, et à moins que l'on ne redirige son exécution, ce comportement reste inchangé. Il existe plusieurs possibilités pour rediriger l'exécution d'un script telles que : les labels, les sauts, les appels de fonction et les sorties.
IV-A. Les labels▲
Les labels sont des adresses relatives se présentant sous forme de chaînes de caractères préfixées par : et terminées par un caractère blanc (l'espace, la tabulation ou le retour à la ligne). Ces adresses relatives pointent vers le premier caractère situé après le retour à la ligne qui termine le label. Elles peuvent être utilisées pour adresser une portion de code. Chaque nom de label devrait être unique dans le script. La syntaxe d'un label est la suivante, où <Label>est le nom du label :
:
<
Label
>
IV-B. Les sauts▲
La commande goto effectue un saut inconditionnel vers le label spécifié en paramètre, permettant ainsi de continuer l'exécution du programme dans une portion de code située n'importe où dans le script. Une fois le saut effectué, l'exécution continue à la ligne qui suit le label spécifié :
goto
[:
]<
Label
>
2.
3.
4.
5.
6.
7.
8.
@
echo
off
goto
:MonLabel
echo
Une phrase qui ne sera jamais affichée.
:MonLabel
echo
Une phrase qui sera affichée.
pause
Dans le script 39, la commande echo Une phrase qui ne sera jamais affichée. n'est pas traitée, l'exécution étant redirigée par la commande goto :MonLabel vers la ligne suivant le label :MonLabel soit la commande echo Une phrase qui sera affichée.
IV-C. Contexte de commande▲
L'exécution de l'interpréteur ou d'un script s'effectue dans un processus hôte auquel le système d'exploitation alloue un espace mémoire. Cet espace mémoire est appelé la pile. Une pile est une zone de mémoire dans laquelle les données sont placées les unes à la suite des autres. Les données qui y sont placées doivent être récupérées dans un ordre particulier : la dernière donnée placée doit être la première à être récupérée. Sur cette pile, le système y « place » ce que l'on appelle un contexte.
Lors de sa création, le contexte se voit attribuer, par le système, une série de variables : les variables d'environnement du système, des variables contenant la commande et les paramètres de la commande ayant généré le contexte et une adresse de sortie du contexte. Ainsi créé, le contexte représente l'environnement dans lequel le script va s'exécuter en lui permettant de disposer de données qui lui sont propres. À chaque nouvelle exécution d'un script ou d'une commande, l'interpréteur crée un nouveau contexte et le place sur la pile. Ce nouveau contexte est appelé contexte descendant.
Les variables d'environnement sont héritées du système lors de la création du processus hôte et se propagent par ascendance successive aux différents contextes descendants. Les variables contenant la commande et les arguments d'appels, elles, sont propres au contexte et ne se propagent jamais aux contextes descendants. L'adresse de sortie du contexte n'est pas accessible en tant que variable et, tout comme les arguments d'appel, est propre au contexte. Son rôle est plus amplement détaillé dans le reste du chapitre.
Certaines commandes s'exécutent dans le contexte en cours alors que d'autres créent leurs propres contextes. C'est, en partie, ce qui fait la différence entre commande interne et externe : les commandes internes sont en fait des fonctions internes de l'interpréteur qu'il est possible d'appeler via un alias de type « nom de commande », alors que les commandes externes sont des exécutables distincts de l'interpréteur. L'exécution d'une commande externe ou d'un script générera toujours un nouveau contexte. Les commandes internes s'exécutent toujours dans le contexte courant. Il faut cependant noter que les commandes internes, même si elles exécutent toujours leurs fonctions principales dans le contexte courant, peuvent créer des contextes descendants afin d'exécuter certaines fonctions comme la boucle for qui génère un contexte initial dans lequel se trouve l'ensemble à traiter et un contexte secondaire, créé à chaque itération de la boucle, dans lequel s'opère le traitement.
IV-C-1. La portée des variables▲
Si le concept de contexte de commande est si important, c'est qu'il influe grandement sur l'utilisation des variables. En effet, chaque contexte est une « fraction » de la pile qui n'est pas accessible lorsque l'exécution s'opère depuis un contexte ascendant. Les variables se propagent par ascendance ; c'est-à-dire que lorsqu'un nouveau contexte est créé, il hérite des variables (et de leurs valeurs) du contexte ascendant (celui à partir duquel il a été créé). Si une variable est modifiée dans un contexte descendant, sa valeur restera inchangée dans le contexte ascendant. Ainsi, faire passer une variable vers un contexte descendant est simple (il suffit de créer le contexte), mais l'inverse s'avère plus compliqué, car il n'est pas possible de modifier une valeur dans un contexte ascendant.
L'interpréteur supporte l'expansion retardée des variables qui permet, dans une certaine mesure, de faire passer une valeur vers un contexte ascendant. Pour cela, le processus hôte, lorsque l'expansion retardée est activée, alloue une seconde zone de mémoire qui prend la forme d'un tas qui est accessible depuis n'importe quel contexte. Contrairement à la pile, il est possible, avec le tas, d'y placer et récupérer les données dans n'importe quel ordre. À chaque fois qu'une variable est créée ou modifiée, elle est placée à la fois dans le contexte (autrement dit sur la pile) et dans le tas. Pour accéder aux variables dont l'expansion est retardée, il faut utiliser le symbole ! au lieu du symbole % lors de leur expansion. Ainsi la variable prendra la dernière valeur qui lui a été attribuée pendant l'exécution et non la valeur qu'elle possède dans le contexte en cours. L'expansion retardée des variables sera plus amplement abordée dans le chapitre VIL'expansion retardée des variables.
IV-D. Les fonctions▲
Les fonctions sont des portions de code « isolées », commençant par un label et finissant par un saut à une adresse spécifique : l'adresse de retour. La vraie différence avec une simple portion de code réside dans le fait qu'un nouveau contexte est créé, permettant ainsi aux fonctions de disposer de paramètres de commande et, dans certains cas, d'un code de sortie.
IV-D-1. L'appel de fonction▲
La commande call permet d'effectuer des appels de fonction. Elle a la particularité de créer un contexte dans lequel va s'exécuter la fonction appelée (la portion de code) ; lors de la création du contexte descendant, une adresse de retour et les arguments d'appel vont être empilés. La syntaxe de la commande call est la suivante, où <Label> est le nom de la fonction à appeler et <paramètre> est le ou les paramètres à passer à la fonction.
call
:
<
label
>
[<
paramètre>
[...]]
Lors d'un appel à une fonction, si l'exécution est redirigée vers une autre adresse que l'adresse de retour et que, par la suite, d'autres appels sont effectués vers cette même fonction, alors la première adresse ne sera pas dépilée, occasionnant une fuite de mémoire. Si un script comporte ce cas, son exécution peut être stoppée par l'interpréteur (si l'utilisation de la pile atteint 90 %) comme le montre le script 40.
2.
3.
4.
5.
@
echo
off
:LabelUn
call
:LabelDeux
:LabelDeux
goto
:LabelUn
IV-D-2. Sortie de fonction▲
La commande goto prend aussi le label :eof (End Of File), qui prend la valeur de l'adresse de retour du contexte en cours, soit la ligne se trouvant immédiatement après le dernier appel effectué.
La commande goto n'ajoute rien dans la pile, ainsi l'exécution d'un goto ne peut occasionner de fuite de mémoire. Dans le cas précis de l'exécution d'un goto :eof, la commande goto incrémente le pointeur de pile de la taille totale des adresses des arguments d'appel et de la taille de l'adresse de retour, permettant à la pile de revenir à l'état d'avant l'appel, puis effectue un saut inconditionnel à l'adresse de retour, soit la ligne suivant le dernier appel. Ainsi, si l'on modifie le script 40 de la manière suivante (script 41), on obtient un script qui ne finit jamais, car il n'y a ni fuite de mémoire (chaque appel de la fonction :LabelDeux finit par goto :eof), ni point de sortie (il y aura toujours une commande à exécuter, l'exécution revient toujours au :LabelUn). Seul l'appui sur les touches Ctrl+C permet de quitter le script.
2.
3.
4.
5.
6.
7.
8.
9.
10.
@
echo
off
rem On ajoute un compteur pour visualiser le parcours effectué.
set
/a "Counter=
0"
:LabelUn
call
:LabelDeux
set
/a "Counter+=
1"
goto
:LabelUn
:LabelDeux
echo
%Counter%
goto
:eof
IV-E. Les paramètres de commande▲
Ils sont passés au contexte lors de sa création et sont accessibles sous la forme de variables spéciales : %n, n étant le numéro d'index du paramètre. En effet, chaque contexte étant généré suite à l'appel d'un script, d'une fonction ou d'une commande, il possède une commande d'appel et, la plupart du temps, de paramètres d'appel. L'index %0 contient le nom du script, de la fonction ou de la commande, l'index %1 contient le paramètre 1, l'index %2 contient le paramètre 2 et ainsi de suite. Le paramètre %* expanse tous les paramètres passés au contexte (%0 n'est pas un paramètre, mais une commande d'appel et n'est donc pas renvoyée par %*). Exemple avec le script 42 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
@
echo
off
call
:MonLabel
Param1 Param2
pause
rem Si tous les contextes créés pendant l'exécution du script ont
rem correctement été dépilés, alors le label «:eof » prend comme
rem valeur l'adresse de sortie du processus hôte.
goto
:eof
:MonLabel
echo
%*
echo
%0
echo
%1
echo
%2
goto
:eof
L'utilisation des paramètres suit plusieurs règles :
- les paramètres ne sont pas limités en nombre dans la commande d'appel, mais la règle des 2048/4096 caractères maximum par ligne de commande s'applique ;
- par souci de compatibilité avec les systèmes Windows XP et antérieurs, il est préférable de ne pas utiliser des paramètres avec un index supérieur à 9, qui restent cependant accessibles via la commande shift (voir section IV.E.2La commande shift).
IV-E-1. Les modificateurs de paramètres de commande▲
Les paramètres de commande supportent plusieurs modificateurs permettant de les parser, en voici la liste exhaustive :
Modificateur |
Description |
%~1 |
renvoie %1 en supprimant les guillemets (") de début et de fin. |
%~f1 |
renvoie le chemin d'accès vérifié du fichier désigné par %1, si le fichier n'est pas trouvé, alors ce modificateur s'expanse en une chaîne vide. |
%~d1 |
renvoie la lettre de lecteur du fichier désigné par %1. |
%~p1 |
renvoie le chemin d'accès du fichier désigné par %1. |
%~n1 |
renvoie le nom du fichier désigné par %1. |
%~x1 |
renvoie l'extension du fichier désigné par %1. |
%~s1 |
renvoie le chemin d'accès, sous forme de noms courts, du fichier désigné par %1. |
%~a1 |
renvoie les attributs du fichier désigné par %1. |
%~t1 |
renvoie la date et l'heure de création du fichier désigné par %1. |
%~z1 |
renvoie la taille du fichier désigné par %1. |
%~$<variable>:1 |
<variable> est parcourue à la recherche d'occurrence de %1, si aucune occurrence de %1 n'est trouvée ou que <variable> n'est pas défini dans le contexte en cours, alors ce modificateur s'expanse en une chaîne vide. Si <variable> est composé de plusieurs chaînes, elles doivent être séparées par des points-virgules (;) afin que le modificateur puisse les distinguer correctement. |
Tableau 10 : Les modificateurs de paramètres de commande.
Les modificateurs de paramètres de commande fonctionnent pour tous les index. Ils peuvent être utilisés conjointement pour obtenir plusieurs informations en même temps, comme le montre le script 43 :
2.
3.
4.
5.
6.
7.
8.
9.
@
echo
off
rem Ce script doit être lancé depuis « cmd.exe » pour obtenir le bon résultat.
cls
call
:MonLabel
%cmdcmdline%
pause
goto
:eof
:MonLabel
echo
%~dpnx$ComSpec:1
goto
:eof
IV-E-2. La commande shift▲
La commande shift permet de décaler l'index de tous les paramètres de -1, afin d'accéder aux index supérieurs à neuf, ou de créer un mécanisme de gestion des paramètres (voir la mise en application de ce chapitreMise en application des contextes). Si les extensions de commande sont activées, la commande shift prend le paramètre /n, où n est le numéro d'index à partir duquel commence le décalage des paramètres, tous les index égaux ou supérieurs à n seront décalés de -1.
shift
[/n]
Dans le script 44, le commande shift /1 permet d'accéder au deuxième paramètre via l'index 1 :.
2.
3.
4.
5.
6.
7.
8.
9.
@
echo
off
cls
call
:MonLabel
"rien" %cmdcmdline%
pause
exit
/b
:MonLabel
shift
/1
echo
%~dpnx$ComSpec:1
goto
:eof
IV-F. Les sorties▲
La sortie d'un contexte ou d'un processus peut s'effectuer via les commandes exit et goto :eof. Dans le cas de goto :eof, si tous les contextes ont correctement été dépilés, alors la dernière adresse dépilée est :
- soit l'adresse de sortie du processus dans le cas d'un script appelé en cliquant dessus ;
- soit l'adresse de retour à l'interpréteur si le script a été lancé depuis celui-ci.
La commande exit, elle, permet de sortir du processus hôte, quel que soit le nombre de contextes empilés. Si la commande exit est utilisée avec le paramètre /b, alors la sortie s'effectuera à l'adresse de sortie du contexte et non à l'adresse de sortie du processus hôte. L'intérêt de la commande exit est qu'un code numérique de sortie peut être spécifié, contrairement à goto :eof. La syntaxe de la commande exit est la suivante, où <CodeSortie> est le code de sortie.
exit
[/b] [<
CodeSortie>
]
Dans le script 45, la sortie de la fonction :LabelDeux s'effectue à l'aide d'un goto:eof et la sortie de la fonction :LabelQuatre s'effectue à l'aide d'exit /b ; dans les deux cas, le retour vers la procédure appelante s'opère correctement, car c'est la bonne adresse de retour qui est dépilée. Dans la fonction :LabelSix du script 45, la commande exit /b provoque la fin du script, car la fonction :LabelSix a été appelée avec la commande goto :LabelSix qui n'a rien empilé. Comme tous les contextes précédents ont été dépilés correctement, seule la dernière adresse de retour subsiste. Cette adresse correspond à l'adresse de retour à l'interpréteur si le script a été appelé depuis celui-ci ou à l'adresse de sortie du processus, si le script a été appelé par un double-clic.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
@
echo
off
cls
set
/a "CounterGoto=
0"
set
/a "CounterExit=
0"
set
/a "CounterCounter=
0"
:LabelUn
call
:LabelDeux
set
/a "CounterGoto+=
1"
echo
CounterGoto: %CounterGoto%
sur affichage Goto
1.
if
%CounterGoto%
EQU
6 goto
:LabelTrois
goto
:LabelUn
:LabelDeux
set
/a "CounterGoto+=
1"
echo
CounterGoto: %CounterGoto%
sur affichage Goto
2.
goto
:eof
:LabelTrois
call
:LabelQuatre
set
/a "CounterExit+=
1"
echo
CounterExit: %CounterExit%
sur affichage Exit
1.
if
%CounterExit%
EQU
6 goto
:LabelCinq
goto
:LabelTrois
:LabelQuatre
set
/a "CounterExit+=
1"
echo
CounterExit: %CounterExit%
sur affichage Exit
2.
exit
/b
:LabelCinq
goto
:LabelSix
set
/a "CounterCounter+=
1"
echo
CounterCounter: %CounterCounter%
sur affichage Counter 1.
if
%CounterCounter%
EQU
6 exit
/b
goto
:LabelCinq
:LabelSix
set
/a "CounterCounter+=
1"
echo
CounterCounter: %CounterCounter%
sur affichage Counter 2.
exit
/b
IV-G. Code de sortie▲
Comme expliqué précédemment, un code de sortie peut être spécifié pour une fonction ou pour un script. Cela se fait via la commande exit [/b] <Code> où <Code> est un code de sortie numérique. Les codes de sortie sont régis par les mêmes règles que les nombres entiers et sont codés sur 32 bits en arithmétique signée. Ce code de sortie est fixé dans la variable ErrorLevel afin de pouvoir être utilisé par la suite. Exemple avec le script 46 :
2.
3.
4.
5.
6.
7.
8.
9.
@
echo
off
echo
%ErrorLevel%
call
:UnLabel
echo
%ErrorLevel%
pause
exit
/b
:UnLabel
exit
/b 1
IV-H. Script batch et adresse de retour▲
L'appel d'un script depuis l'interpréteur ou en cliquant dessus génère toujours un contexte complet. Cependant l'appel d'un script en ligne de commande depuis un autre script génère un contexte ne possédant pas d'adresse de retour. Exemple avec le script 47 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
@
echo
off
if
exist
test.bat del
/q test.bat
rem "^>^>test.bat" est une redirection et sera expliquée au chapitre VII.
call
:WriteTestBat
>>
test.bat
test.bat
echo
Retour à %~nx0
.
del
/q test.bat
pause
exit
/b
:WriteTestBat
echo
@
echo
off
echo
echo
Une phrase affichée dans test.bat
echo
pause
goto
:eof
Lorsqu'on exécute le script 47, celui-ci crée un second script, test.bat. L'appel en ligne de commande Test.bat affiche alors Une phrase affichée depuis test.bat et met en pause l'exécution. Lorsqu'on appuie sur une touche, cela provoque la fin des deux scripts. En effet, quand l'interpréteur arrive à la fin d'un script et que celui-ci ne se termine pas par goto :eof ou exit [/b], il effectue de lui-même le saut à l'adresse de retour. Sauf que l'appel de test.bat n'a pas empilé d'adresse de retour et de ce fait, l'adresse qui est dépilée est celle du premier script. Pour parer à ce problème, il faut s'assurer que l'adresse de retour soit effectivement empilée en utilisant la commande call. Ainsi, en modifiant le script 47 de la manière suivante (script 48), l'appel et la sortie s'effectuent sans erreur.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
@
echo
off
if
exist
test.bat del
/q test.bat
rem "^>^>test.bat" est une redirection et sera expliquée au chapitre VII.
call
:WriteTestBat
>>
test.bat
call
test.bat
echo
Retour à %~nx0
.
del
/q test.bat
pause
exit
/b
:WriteTestBat
echo
@
echo
off
echo
echo
Une phrase affichée dans test.bat
echo
pause
goto
:eof
IV-I. La commande start▲
La commande start permet de lancer un script ou une commande dans un nouveau processus, permettant ainsi d'exécuter des commandes dans un environnement modifié. Ainsi, toute variable créée ou modifiée dans le script appelant est passée à ce nouveau processus.
start
["<
caption>
"] [/d <
path
>
] [/i] [/min] [/max] [/separate|
/shared]
[/low|
/normal|
/high|
/realtime|
/abovenormal|
/belownormal] [/affinity <
hexa>
]
[/wait] [/b] [<
commande>
] [<
paramètres>
]
Paramètres |
Descriptions |
<caption> |
Titre de la fenêtre. |
/d |
Spécifie que le chemin d'accès donné par <path> est le répertoire de départ. |
<path> |
Chemin d'accès du répertoire de départ. |
/b |
Lance l'application dans la fenêtre courante (en tâche de fond). L'arrêt par Ctrl+C est remplacé par Ctrl+Pause. |
/i |
Le nouveau contexte sera le contexte original du processus hôte et non le contexte en cours. |
/min |
Démarrer dans une fenêtre réduite. |
/max |
Démarrer dans une fenêtre agrandie. |
/separate |
Démarrer les programmes 16 bits dans un espace mémoire distinct. Ne fonctionne pas sur les systèmes 64 bits. |
/shared |
Démarrer les programmes 16 bits dans un espace mémoire partagé. Ne fonctionne pas sur les systèmes 64 bits. |
/low |
Démarrer l'application dans la classe de priorité IDLE. |
/normal |
Démarrer l'application dans la classe de priorité NORMAL. |
/high |
Démarrer l'application dans la classe de priorité HIGH. |
/realtime |
Démarrer l'application dans la classe de priorité REALTIME. |
/abovenormal |
Démarrer l'application dans la classe de priorité ABOVENORMAL. |
/belownormal |
Démarrer l'application dans la classe de priorité BELOWNORMAL. |
/affinity |
La nouvelle application aura le masque d'affinité de processeur spécifié, exprimé en tant que valeur hexadécimale. |
<hexa> |
Affinité du processus sous forme de valeur hexadécimale. |
/wait |
Lancer la commande et attendre qu'elle soit finie pour continuer l'exécution. S'il s'agit d'une commande interne ou d'un fichier batch, la fenêtre reste ouverte après l'exécution de la commande. |
<commande> |
Commande à exécuter. |
<paramètres> |
Paramètres à passer à la commande. |
Tableau 11 : Les paramètres de la commande start.
Le script 49 appelle une nouvelle instance de lui-même avec le paramètre foo après avoir défini la variable X, la seconde instance affiche bien la valeur de X alors qu'elle n'a pas déclaré X. Notez bien qu'il s'agit d'un nouveau processus, il est donc possible de le quitter avec la commande exit sans aucun paramètre.
2.
3.
4.
5.
6.
7.
8.
9.
10.
@
echo
off
if
"%1
"==
"foo" goto
SecondInstance
set
"X=
bar"
start
%~nx0
foo
pause
exit
/b
:SecondInstance
echo
%X%
pause
exit
Première instance :
Seconde instance :
Si l'on modifie le script 49 en ajoutant le paramètre /i dans la commande start (script 50), alors le nouveau processus sera créé en héritant du contexte original du processus hôte dans lequel la variable X n'a pas été déclarée.
2.
3.
4.
5.
6.
7.
8.
9.
10.
@
echo
off
if
"%1
"==
"foo" goto
SecondInstance
set
"X=
bar"
start
/i %~nx0
foo
pause
exit
/b
:SecondInstance
echo
%X%
pause
exit
Première instance :
Seconde instance :
Une autre application intéressante de la commande start est l'exécution parallèle à l'aide du paramètre /b comme dans le script 51.
N.B. : la commande timeout n'est pas fournie en standard sur tous les systèmes Windows. Si vous ne l'avez pas, veuillez supprimer la commande timeout du script, dé-commenter les commandes echo, ping et supprimer le caractère d'échappement dans la commande ping.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
@
echo
off
if
"%1
"==
"foo" goto
SecondInstance
start
/b %~nx0
foo
::echo Attendez quelques instant ...
::ping -n 15 localhost^>nul
timeout
/t 10 /nobreak
pause
exit
/b
:SecondInstance
echo
%1
exit
IV-J. La commande setlocal▲
Cette commande permet de modifier partiellement les contextes en créant un contexte local. Les variables et leurs valeurs, dans ce nouveau contexte local, sont soumises aux règles inhérentes à la création de contexte. L'avantage de cette commande est que l'on peut créer un contexte sans passer par un appel. Ce contexte local ne possède pas d'adresse de retour ni de paramètres d'appel. Seules les variables, et éventuellement les extensions de commande, sont affectées. Ainsi, toutes les variables créées ou modifiées dans ce contexte local y sont propres. Cette modification prend fin lorsque l'exécution rencontre la commande endlocal. La commande setlocal prend aussi les paramètres :
- enabledelayedexpansion qui active l'expansion retardée ;
- disabledelayedexpansion qui désactive l'expansion retardée ;
- enableextensions qui active les extensions de commande ;
- disableextensions qui désactive les extensions de commande.
setlocal
[enableextensions|
disableextensions] [enabledelayedexpansion|
disabledelayedexpansion]
IV-K. La commande endlocal▲
Cette commande termine le contexte local généré par la commande setlocal correspondante. Plusieurs contextes locaux pouvant être empilés les uns au-dessus des autres, toutes les modifications apportées dans le contexte local qui se termine sont perdues, y compris les modifications apportées par les paramètres de la commande setlocal. La commande endlocal ne prend aucun paramètre. Exemple pour setlocal et endlocal avec le script 52 :
2.
3.
4.
5.
6.
7.
8.
@
echo
off
setlocal
set
"X=
foo"
echo
%X%
endlocal
echo
%X%
pause
exit
IV-L. Mise en application des contextes▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
@
echo
off
set
"ScriptName=
%~nx0
"
set
"NegNum=
false"
set
/a "PrefixEnable=
0"
set
/a "UpperPrefix=
0"
setlocal
:ParseArgs
if
"%~1
"==
"" goto
Init
if
/i "%~1
"==
"/?
" goto
Help
if
/i "%~1
"==
"-?
" goto
Help
if
/i "%~1
"==
"/h" goto
Help
if
/i "%~1
"==
"-h" goto
Help
if
/i "%~1
"==
"/help
" goto
Help
if
/i "%~1
"==
"-help" goto
Help
if
/i "%~1
"==
"/hex" (
set
"SInt32=
%~2
"
set
/a "SInt32"
if
errorlevel
1 (
shift
/1
goto
BadSyntax
)
shift
/1
shift
/1
goto
ParseArgs
)
if
/i "%~1
"==
"/p" (
set
/a "PrefixEnable=
1"
if
%~1
EQU
/P set
/a "UpperPrefix=
1"
shift
/1
goto
ParseArgs
)
:BadSyntax
echo.
rem La commande 'net helpmsg' affiche un message prédéfini dans
rem la langue d'installation du système d'exploitation
net
helpmsg 87
echo
[ %~1
]
echo.
if
not
"%~0
"==
":BadSyntax
" endlocal
exit
/b 1
:Init
if
defined
SInt32 goto
Exec
:UnknowError
call
:BadSyntax
/hex
call
:Help
endlocal
exit
/b 2
:Exec
rem Si le nombre est négatif, on utilise son complément à un et on le définit comme négatif.
if
%SInt32%
LSS
0 (
set
"NegNum=
true"
set
/a "SInt32=
~SInt32"
)
rem On utilise un algorithme de divisions successives par 16 pour obtenir des
rem nombres en base 16, chaque nombre obtenu forme un quartet.
set
/a "Nibble0=
SInt32 %% 16","HighOrder0=
SInt32 / 16"
set
/a "Nibble1=
HighOrder0 %% 16","HighOrder1=
HighOrder0 / 16"
set
/a "Nibble2=
HighOrder1 %% 16","HighOrder2=
HighOrder1 / 16"
set
/a "Nibble3=
HighOrder2 %% 16","HighOrder3=
HighOrder2 / 16"
set
/a "Nibble4=
HighOrder3 %% 16","HighOrder4=
HighOrder3 / 16"
set
/a "Nibble5=
HighOrder4 %% 16","HighOrder5=
HighOrder4 / 16"
set
/a "Nibble6=
HighOrder5 %% 16"
set
/a "Nibble7=
HighOrder5 / 16"
rem Si c'est un nombre négatif^ ("%NegNum%"=="true"^),
rem on retranche chaque valeur à 15 pour récupérer le nombre original.
if
"%NegNum%
"==
"true" (
set
/a "Nibble0=
15 Nibble0"
set
/a "Nibble1=
15 Nibble1"
set
/a "Nibble2=
15 Nibble2"
set
/a "Nibble3=
15 Nibble3"
set
/a "Nibble4=
15 Nibble4"
set
/a "Nibble5=
15 Nibble5"
set
/a "Nibble6=
15 Nibble6"
set
/a "Nibble7=
15 Nibble7"
)
rem On transforme chaque quartet en notation hexadécimale.
if
%Nibble0%
GTR
9 (
if
%Nibble0%
EQU
10 set
"Nibble0=
A"
if
%Nibble0%
EQU
11 set
"Nibble0=
B"
if
%Nibble0%
EQU
12 set
"Nibble0=
C"
if
%Nibble0%
EQU
13 set
"Nibble0=
D"
if
%Nibble0%
EQU
14 set
"Nibble0=
E"
if
%Nibble0%
EQU
15 set
"Nibble0=
F"
)
if
%Nibble1%
GTR
9 (
if
%Nibble1%
EQU
10 set
"Nibble1=
A"
if
%Nibble1%
EQU
11 set
"Nibble1=
B"
if
%Nibble1%
EQU
12 set
"Nibble1=
C"
if
%Nibble1%
EQU
13 set
"Nibble1=
D"
if
%Nibble1%
EQU
14 set
"Nibble1=
E"
if
%Nibble1%
EQU
15 set
"Nibble1=
F"
)
if
%Nibble2%
GTR
9 (
if
%Nibble2%
EQU
10 set
"Nibble2=
A"
if
%Nibble2%
EQU
11 set
"Nibble2=
B"
if
%Nibble2%
EQU
12 set
"Nibble2=
C"
if
%Nibble2%
EQU
13 set
"Nibble2=
D"
if
%Nibble2%
EQU
14 set
"Nibble2=
E"
if
%Nibble2%
EQU
15 set
"Nibble2=
F"
)
if
%Nibble3%
GTR
9 (
if
%Nibble3%
EQU
10 set
"Nibble3=
A"
if
%Nibble3%
EQU
11 set
"Nibble3=
B"
if
%Nibble3%
EQU
12 set
"Nibble3=
C"
if
%Nibble3%
EQU
13 set
"Nibble3=
D"
if
%Nibble3%
EQU
14 set
"Nibble3=
E"
if
%Nibble3%
EQU
15 set
"Nibble3=
F"
)
if
%Nibble4%
GTR
9 (
if
%Nibble4%
EQU
10 set
"Nibble4=
A"
if
%Nibble4%
EQU
11 set
"Nibble4=
B"
if
%Nibble4%
EQU
12 set
"Nibble4=
C"
if
%Nibble4%
EQU
13 set
"Nibble4=
D"
if
%Nibble4%
EQU
14 set
"Nibble4=
E"
if
%Nibble4%
EQU
15 set
"Nibble4=
F"
)
if
%Nibble5%
GTR
9 (
if
%Nibble5%
EQU
10 set
"Nibble5=
A"
if
%Nibble5%
EQU
11 set
"Nibble5=
B"
if
%Nibble5%
EQU
12 set
"Nibble5=
C"
if
%Nibble5%
EQU
13 set
"Nibble5=
D"
if
%Nibble5%
EQU
14 set
"Nibble5=
E"
if
%Nibble5%
EQU
15 set
"Nibble5=
F"
)
if
%Nibble6%
GTR
9 (
if
%Nibble6%
EQU
10 set
"Nibble6=
A"
if
%Nibble6%
EQU
11 set
"Nibble6=
B"
if
%Nibble6%
EQU
12 set
"Nibble6=
C"
if
%Nibble6%
EQU
13 set
"Nibble6=
D"
if
%Nibble6%
EQU
14 set
"Nibble6=
E"
if
%Nibble6%
EQU
15 set
"Nibble6=
F"
)
if
%Nibble7%
GTR
9 (
if
%Nibble7%
EQU
10 set
"Nibble7=
A"
if
%Nibble7%
EQU
11 set
"Nibble7=
B"
if
%Nibble7%
EQU
12 set
"Nibble7=
C"
if
%Nibble7%
EQU
13 set
"Nibble7=
D"
if
%Nibble7%
EQU
14 set
"Nibble7=
E"
if
%Nibble7%
EQU
15 set
"Nibble7=
F"
)
rem Les quartets 0 à 3 forment le Least Significant Word.
set
"LSW=
%Nibble3%%Nibble2%%Nibble1%%Nibble0%
"
rem Les quartets 4 à 7 forment le Most Significant Word.
set
"MSW=
%Nibble7%%Nibble6%%Nibble5%%Nibble4%
"
rem On affiche le résultat.
if
%PrefixEnable%
EQU
1 (
if
%UpperPrefix%
EQU
1 (
echo
0X%MSW%%LSW%
) else
(
echo
0x%MSW%%LSW%
)
) else
(
echo
%MSW%%LSW%
)
goto
End
:Help
echo.
echo
%ScriptName%
[/p^|
/P] /hex ^<
number^>
echo
%ScriptName%
{/?
^|
-?
^|
/h^|
-h^|
/help
^|
-help}
echo.
echo
/hex Définit le ^<
number^>
qui doit être exprimé en hexadécimal.
echo
/p Définit que le préfixe doit être affiché en minuscules.
echo
/P Définit que le préfixe doit être affiché en majuscules.
echo.
/?
Affiche cette aide.
echo.
:End
if
not
"%~0
"==
":Help
" endlocal
exit
/b 0
Le script 53 doit être appelé avec des paramètres afin de fonctionner. Ainsi, quand on appelle ce script avec l'un des paramètres suivants : /?, -?, /h, -h, /help ou -help, l'aide est affichée. Si on appelle le script avec les paramètres /hex n (ou n est le nombre voulu), il affiche la représentation hexadécimale de ce nombre. Notez également l'utilisation du paramètre %~nx0 pour définir le nom du script, ici, la fonction :Help est appelée via un call au label :UnknowError (la commande exit /b 0 du label :End fournit le saut à l'adresse de retour), %0 aurait été, alors, la chaîne :Help.
Un script devrait toujours avoir un squelette similaire à celui du script 53. Pour en connaître la raison, examinons ses différentes parties :
- le script commence par @echo off pour rendre plus net l'affichage, puis les variables de configuration sont initialisées avec leurs valeurs par défaut, ce qui permet au script de fonctionner même si ces valeurs ne sont pas modifiées par la suite. La commande setlocal est utilisée pour les cas d'erreur, par exemple si le nombre donné via l'argument /hex dépasse 32 bits. La variable ErrorLevel n'est pas remise à jour suite à une erreur dans une commande interne, ainsi, si l'on appelle le script depuis l'interpréteur avec en paramètre un nombre invalide, tous les appels du script qui suivront, même avec un nombre valide, se termineront sur une erreur. Pensez à utiliser la commande endlocal avant chaque point de sortie du script ;
- vient ensuite le label :ParseArgs dans lequel les paramètres d'appel vont être vérifiés, chaque fois qu'un paramètre est trouvé, sa valeur est définie dans la variable correspondante puis les paramètres d'appel sont décalés avant le retour au label :ParseArgs. Si le paramètre fourni dans la commande d'appel n'est pas trouvé, l'exécution continue jusqu'au label :BadSyntax qui affiche un message d'erreur et quitte le script avec le code d'erreur 1 ;
- une fois tous les paramètres lus, l'exécution est redirigée vers le label :Init, qui a pour fonction de vérifier que les données de travail ont bien été fournies dans la commande d'appel. En effet, ce script appelé sans paramètre s'exécute au moins jusqu'au label :Init. Dans ce label, il convient, en général, de modifier les variables de configuration en fonction des valeurs fournies dans la commande d'appel. Si les données fournies ne sont pas valides, l'exécution continue au label :UnknowError qui, dans notre cas, va afficher où se trouve l'erreur ainsi que l'aide ;
- si les données de travail sont valides, l'exécution est redirigée vers le label :Exec qui va exécuter le travail requis puis afficher le résultat avant d'être redirigée vers le label :End ;
- le label :Help fournit une aide en ligne de commande (c'est toujours utile). Celui-ci se trouvant juste devant le label :End, il peut à la fois être utilisé comme une fonction ou comme une portion de code classique. Notez l'échappement de la commande echo avec le point à la ligne echo. /? Affiche cette aide. sans quoi l'interpréteur aurait affiché l'aide de la commande echo.
V. Les boucles▲
Les boucles sont gérées par la commande for, elle permettent une grande quantité d'actions sur des fichiers, des répertoires ou des chaînes de caractères. La boucle for se compose d'un ensemble sur lequel s'opère une commande. L'<ensemble> est parsé puis transite au moyen d'une <variable> vers la <commande>. La syntaxe de base de la boucle for est la suivante.
for
%<
variable>
in
(<
ensemble>
) do
<
commande>
La <variable> est en fait un paramètre de commande propre à la boucle for. Elle utilise donc la syntaxe et les modificateurs de paramètre de commande (voir section IV.E.1Les modificateurs de paramètres de commande pour plus d'information), à ceci près que le paramètre est désigné par une lettre et que le caractère % du paramètre doit être échappé lorsque la boucle est utilisée dans un script. Ainsi dans un script, le paramètre %A doit être utilisé avec le caractère d'échappement %, ce qui nous donne la syntaxe %%A. Il faut aussi noter que les paramètres de la boucle for respectent la casse, ainsi %a est différent de %A.
L'<ensemble> est composé d'une ou plusieurs entrées. Si plusieurs entrées sont présentes, elles doivent être séparées par des virgules pour permettre à la boucle de les distinguer correctement. Cet <ensemble> peut être :
- soit des noms de fichiers (avec leurs chemins d'accès s'ils ne sont pas dans le répertoire courant) ;
- soit des chemins d'accès ;
- soit des chaînes de caractères.
Toutes les composantes de l'<ensemble> sont passées à la <commande> sous forme de chaînes de caractères. Seule l'utilisation qui en est faite dans le traitement détermine s'il s'agit d'une chaîne de caractères, d'un nom de fichier ou d'un chemin d'accès. Si une chaîne de l'<ensemble> contient un ou plusieurs espaces, elle doit être placée entre guillemets. Exemple avec le script 54 :
2.
3.
4.
5.
@
echo
off
for
%%A
in
(texte) do
echo
%%A
for
%%B
in
("%cd%
") do
if
exist
%%B
echo
%%B
pause
exit
/b
Si les extensions de commande sont activées, la boucle for peut prendre un des paramètres de la liste suivante :
- /d ;
- /r ;
- /l ;
- /f.
V-A. Les boucles pour chemin d'accès▲
Les boucles pour chemin d'accès permettent d'effectuer des recherches de dossiers ou de fichiers. La recherche de dossier s'effectue avec le paramètre /d tandis que la recherche de fichier s'effectue avec le paramètre /r. Leurs syntaxes sont les suivantes :
for
/d %<
paramètre>
in
(["][<
chemin_d'accès>
\]{*|<
motif>
}["]) do
<
cmd
>
for
/r ["][<
chemin_d'accès>
\]["] %<
paramètre>
in
(["]{*|<
motif>|
.}["]) do
<
cmd
>
Options |
Descriptions |
<chemin_d'accès> |
Chemin d'accès de base de la recherche. Si aucun chemin n'est fourni dans la boucle, alors la recherche se porte sur le répertoire courant. |
* |
Caractère générique. Remplace plusieurs caractères. |
? |
Caractère générique. Remplace un seul caractère. |
<motif> |
Le motif définit le nom du fichier qu'il faut chercher, il peut être composé de tout caractère, y compris les caractères génériques. |
. |
Avec le paramètre /r, le point, s'il est utilisé seul, signifie que la recherche porte sur les noms de dossier. |
Tableau 12 : Paramètres des boucles pour chemins d'accès
Exemple avec le script 55.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
@
echo
off
mkdir
"%cd%
\foo"
mkdir
"%cd%
\foo\bar"
mkdir
"%cd%
\foo\beer"
call
:WriteFile
>>
"%cd%
\foo\bar\bar.bat"
call
:WriteFile
>>
"%cd%
\foo\bar\bar.txt"
call
:WriteFile
>>
"%cd%
\foo\beer\beer.bat"
call
:WriteFile
>>
"%cd%
\foo\beer\beer.txt"
echo
Liste des dossiers:
for
/d %%A
in
("%cd%
\foo\*
") do
echo
%%A
echo.
echo
Liste des scripts batch:
for
/r "%cd%
\foo" %%B
in
("*
.bat") do
echo
%%~B
echo.
pause
rd
/s /q "%cd%
\foo"
exit
/b
:WriteFile
echo
@
echo
off
echo
pause
goto
:eof
V-B. Les boucles pour compteurs▲
Le paramètre /l permet d'effectuer une boucle avec comme paramètre un compteur numérique, ainsi l'<ensemble> prend la forme : <origine>, <pas_incrémentiel>, <fin>. La boucle commencera le traitement avec comme paramètre un nombre ayant pour valeur <origine> puis, après chaque itération de la boucle, sera ajouté <pas_incrémentiel> tant que <fin> n'est pas atteinte. Lorsque <fin> est atteinte la boucle exécute sa dernière itération. <origine>, <pas_incrémentiel> et <fin> peuvent être séparés, soit par des espaces, soit par des virgules.
for
/l %<
paramètre>
in
(<
origine>
<
pas_incrémentiel>
<
fin>
) do
<
cmd
>
for
/l %<
paramètre>
in
(<
origine>
,<
pas_incrémentiel>
,<
fin>
) do
<
cmd
>
2.
3.
4.
@
echo
off
for
/l %%A
in
(1 1 20) do
echo
Le paramètre A a pour valeur %%A
.
pause
exit
/b
Comme les nombres dans l'interpréteur ont une définition de 32 bits et utilisent tous l'arithmétique signée, les limites de la boucle for /l sont celles des nombres entiers signés. Ainsi l'<origine>, le <pas_incrémentiel> et la <fin> peuvent tous être compris entre -2147483648 et +2147483647. Le script 57 fonctionne donc parfaitement, mais, comme vous pouvez l'imaginer, son exécution est très longue.
2.
3.
4.
@
echo
off
for
/l %%A
in
(2147483647 -1 -2147483648) do
echo
Le paramètre A a pour valeur %%A
.
pause
exit
/b
V-C. Les boucles de recherche▲
Le paramètre /f indique à la boucle for que le traitement peut s'opérer sur des chaînes de caractères, des fichiers ou des commandes, mais à des fins de recherche. Ce paramètre a la particularité de prendre des options supplémentaires permettant de parser l'<ensemble>.
for
/f ["<
options>
"] %<
paramètre>
in
(<
ensemble>
) do
<
commande>
Les options prises en charge par for /f sont les suivantes :
Options |
Descriptions |
eol=c |
Définit le préfixe de commentaire, qui contrairement à ce que suggère son nom (eol : « End Of Line », ou fin de ligne) doit se trouver en début de ligne. Tout ce qui se trouve après le caractère spécifié sera ignoré par la boucle for. Il est recommandé d'utiliser le plus souvent possible le caractère ; pour la compatibilité avec les fichiers *.ini et *.inf (sous Windows), ou le caractère # si le script est susceptible de traiter des fichiers de type Unix. |
skip=n |
Nombre de lignes à ignorer au début de l'ensemble. |
delims=xxx |
Délimiteurs, par défaut : l'espace et la tabulation. Cette option doit toujours être donnée en dernière position afin de pouvoir spécifier un retour à la ligne comme délimiteur. Cela se fait en écrivant l'option delims avec le symbole égal directement suivi du guillemet fermant du bloc d'options. |
tokens=x,y-z* |
Spécifie l'index des jetons devant être transmis au corps de la boucle for. Chaque jeton étant une chaîne se trouvant entre deux délimiteurs définis par l'option delims. La forme y-z définit une étendue allant des jetons y à z. Si le dernier caractère de l'option tokens est un astérisque (*), alors une variable supplémentaire est allouée et recevra tout le texte qui se trouve après le dernier jeton (y compris les délimiteurs qui pourraient si trouver). Chaque jeton sera attribué à un paramètre de la boucle for en partant de celui spécifié dans la boucle et dans l'ordre alphabétique. Si le paramètre %A est spécifié avec l'option tokens=1-3*, alors le premier jeton sera accessible dans le corps de la boucle via le paramètre %A, le second via le paramètre %B, le troisième via le paramètre %C et enfin le reste de la ligne via le paramètre %D. |
usebackq |
Spécifie que la nouvelle sémantique est en place, chaque chaîne se trouvant entre guillemets simples inversés (`) est exécutée en tant que commande et une chaîne entre guillemets simples (') est une chaîne de caractères. La nouvelle sémantique permet ainsi l'utilisation de guillemets doubles (") pour citer des noms de fichiers ou pour utiliser une chaîne qui contient des guillemets doubles ("). |
Tableau 13 : Paramètres de la boucle « for /f »
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
@
echo
off
setlocal
enabledelayedexpansion
:: La chaîne « 1^>^>"%cd%\test.txt" » est une redirection et sera expliquée au chapitre VII.
1>
"%cd%
\test.txt" echo
Ligne 1
1>>
"%cd%
\test.txt" echo
Ligne 2
1>>
"%cd%
\test.txt" echo
Ligne 3
1>>
"%cd%
\test.txt" echo
Ligne 4
1>>
"%cd%
\test.txt" echo
Ligne 5
1>>
"%cd%
\test.txt" echo
;un petit commentaire
for
/f "eol=
; delims=
" %%A
in
('type
test.txt') do
echo
%%A
echo.
for
/f "eol=
; skip=
4 delims=
" %%A
in
('type
test.txt') do
echo
%%A
del
/Q test.txt
echo.
for
/f "tokens=
1-5*
" %%A
in
("1 2 3 4 5 6 7 8 9") do
(
echo
%%A
echo
%%B
echo
%%C
echo
%%D
echo
%%E
echo
%%F
)
echo.
for
/f "usebackq delims=
" %%A
in
(`type
"%cd%
\%~nx0
"`) do
(
echo
%%A
)
echo.
pause
exit
/b
Plusieurs points sont à noter dans le script 58 :
- le premier est l'utilisation de l'option eol=; qui supprime la chaîne ;un petit commentaire des résultats obtenus des deux premières boucles ;
- le second est l'utilisation de l'option skip=4 qui supprime les quatre premières lignes du résultat de la seconde boucle ;
- le troisième est l'absence de l'option delims dans la troisième boucle, en effet l'espace et la tabulation sont les délimiteurs par défaut ;
- le dernier est l'utilisation de l'option usebackq et des caractères ` (guillemet simple inversé) qui permet l'utilisation de guillemets doubles pour le chemin d'accès.
V-D. Les échappements propres à la boucle for▲
La boucle for possède un certain nombre de caractères significatifs tel que " ' ` ,. Si les guillemets et autres apostrophes peuvent être gérés avec un bon jeu d'options dans la boucle, il n'en est pas de même avec la virgule. Ainsi, considérons le script suivant qui devrait permettre de trouver les volumes qui sont des lecteurs de CD/DVD.
2.
3.
4.
5.
6.
@
echo
off
for
/f "usebackq skip=
1 tokens=
1,2" %%A
in
(`wmic
volume where
"DriveType=
\"5\"" get DriveLetter,Capacity`) do
(
echo
%%A
%%B
)
pause
exit
/b 0
Ce script se finit sur une erreur, car les guillemets simples inversés modifient la façon dont la chaîne de l'ensemble est prise en compte et de ce fait, la virgule provoque l'erreur. Il faut donc échapper la virgule avec le caractère ^ pour qu'elle soit incluse dans la chaîne sans être traitée par la boucle comme le montre le script 60.
2.
3.
4.
5.
6.
@
echo
off
for
/f "usebackq skip=
1 tokens=
1,2" %%A
in
(`wmic
volume where
"DriveType=
\"5\"" get DriveLetter^,Capacity`) do
(
echo
%%A
%%B
)
pause
exit
/b 0
L'<ensemble>, qu'il soit une commande ou autre chose, est toujours traité dans un premier temps comme une chaîne. Ainsi les opérateurs de redirection (voir chapitre VIILa gestion des flux) doivent aussi être échappés.
2.
3.
4.
5.
6.
7.
8.
9.
10.
@
echo
off
1>
test.txt echo
foo
1>>
test.txt echo
bar
1>>
test.txt echo
foobar
for
/f "usebackq delims=
" %%A
in
(`type
test.txt ^|
find
"foo"`) do
(
echo
%%A
)
del
/q test.txt
pause
exit
/b 0
V-E. Mise en application des boucles▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
@
echo
off
set
"ScriptName=
%~nx0
"
set
"NegNum=
false"
set
/a "PrefixEnable=
0"
set
/a "UpperPrefix=
0"
setlocal
:ParseArgs
if
"%~1
"==
"" goto
Init
if
/i "%~1
"==
"/?
" goto
Help
if
/i "%~1
"==
"-?
" goto
Help
if
/i "%~1
"==
"/h" goto
Help
if
/i "%~1
"==
"-h" goto
Help
if
/i "%~1
"==
"/help
" goto
Help
if
/i "%~1
"==
"-help" goto
Help
if
/i "%~1
"==
"/hex" (
set
"Nibble7=
%~2
"
set
/a "Nibble7"
if
errorlevel
1 (
shift
/1
goto
BadSyntax
)
shift
/1
shift
/1
goto
ParseArgs
)
if
/i "%~1
"==
"/p" (
set
/a "PrefixEnable=
1"
if
%~1
EQU
/P set
/a "UpperPrefix=
1"
shift
/1
goto
ParseArgs
)
:BadSyntax
echo.
for
/f "delims=
" %%A
in
('net
helpmsg 87') do
echo
[ %~1
] %%A
echo.
if
not
"%~0
"==
":BadSyntax
" endlocal
exit
/b 1
:Init
if
defined
Nibble7 goto
Exec
:UnknowError
call
:BadSyntax
/hex
call
:Help
endlocal
exit
/b 2
:Exec
rem Si le nombre est négatif, on utilise son complément à un et on le définit comme négatif.
if
%Nibble7%
LSS
0 (
set
/a "Nibble7=
~Nibble7"
set
"NegNum=
true"
)
rem Divisions successives par 16
for
/l %%A
in
(0 1 6) do
set
/a "Nibble%%A
=
Nibble7 %% 16", "Nibble7/=
16"
rem Si le nombre est négatif, la valeur de chaque chiffre ^(Nibble^) est retranchée à 15.
if
/i "%NegNum%
"==
"true" for
/l %%A
in
(0 1 7) do
set
/a "Nibble%%A
=
15 - Nibble%%A
"
rem Chaque chiffre ayant une valeur supérieure à 9 est transformé en sa représentation hexadécimale.
if
%Nibble0%
GTR
9 (
if
%Nibble0%
EQU
10 set
"Nibble0=
A"
if
%Nibble0%
EQU
11 set
"Nibble0=
B"
if
%Nibble0%
EQU
12 set
"Nibble0=
C"
if
%Nibble0%
EQU
13 set
"Nibble0=
D"
if
%Nibble0%
EQU
14 set
"Nibble0=
E"
if
%Nibble0%
EQU
15 set
"Nibble0=
F"
)
if
%Nibble1%
GTR
9 (
if
%Nibble1%
EQU
10 set
"Nibble1=
A"
if
%Nibble1%
EQU
11 set
"Nibble1=
B"
if
%Nibble1%
EQU
12 set
"Nibble1=
C"
if
%Nibble1%
EQU
13 set
"Nibble1=
D"
if
%Nibble1%
EQU
14 set
"Nibble1=
E"
if
%Nibble1%
EQU
15 set
"Nibble1=
F"
)
if
%Nibble2%
GTR
9 (
if
%Nibble2%
EQU
10 set
"Nibble2=
A"
if
%Nibble2%
EQU
11 set
"Nibble2=
B"
if
%Nibble2%
EQU
12 set
"Nibble2=
C"
if
%Nibble2%
EQU
13 set
"Nibble2=
D"
if
%Nibble2%
EQU
14 set
"Nibble2=
E"
if
%Nibble2%
EQU
15 set
"Nibble2=
F"
)
if
%Nibble3%
GTR
9 (
if
%Nibble3%
EQU
10 set
"Nibble3=
A"
if
%Nibble3%
EQU
11 set
"Nibble3=
B"
if
%Nibble3%
EQU
12 set
"Nibble3=
C"
if
%Nibble3%
EQU
13 set
"Nibble3=
D"
if
%Nibble3%
EQU
14 set
"Nibble3=
E"
if
%Nibble3%
EQU
15 set
"Nibble3=
F"
)
if
%Nibble4%
GTR
9 (
if
%Nibble4%
EQU
10 set
"Nibble4=
A"
if
%Nibble4%
EQU
11 set
"Nibble4=
B"
if
%Nibble4%
EQU
12 set
"Nibble4=
C"
if
%Nibble4%
EQU
13 set
"Nibble4=
D"
if
%Nibble4%
EQU
14 set
"Nibble4=
E"
if
%Nibble4%
EQU
15 set
"Nibble4=
F"
)
if
%Nibble5%
GTR
9 (
if
%Nibble5%
EQU
10 set
"Nibble5=
A"
if
%Nibble5%
EQU
11 set
"Nibble5=
B"
if
%Nibble5%
EQU
12 set
"Nibble5=
C"
if
%Nibble5%
EQU
13 set
"Nibble5=
D"
if
%Nibble5%
EQU
14 set
"Nibble5=
E"
if
%Nibble5%
EQU
15 set
"Nibble5=
F"
)
if
%Nibble6%
GTR
9 (
if
%Nibble6%
EQU
10 set
"Nibble6=
A"
if
%Nibble6%
EQU
11 set
"Nibble6=
B"
if
%Nibble6%
EQU
12 set
"Nibble6=
C"
if
%Nibble6%
EQU
13 set
"Nibble6=
D"
if
%Nibble6%
EQU
14 set
"Nibble6=
E"
if
%Nibble6%
EQU
15 set
"Nibble6=
F"
)
if
%Nibble7%
GTR
9 (
if
%Nibble7%
EQU
10 set
"Nibble7=
A"
if
%Nibble7%
EQU
11 set
"Nibble7=
B"
if
%Nibble7%
EQU
12 set
"Nibble7=
C"
if
%Nibble7%
EQU
13 set
"Nibble7=
D"
if
%Nibble7%
EQU
14 set
"Nibble7=
E"
if
%Nibble7%
EQU
15 set
"Nibble7=
F"
)
if
%PrefixEnable%
EQU
1 (
if
%UpperPrefix%
EQU
1 (
echo
0X%Nibble7%%Nibble6%%Nibble5%%Nibble4%%Nibble3%%Nibble2%%Nibble1%%Nibble0%
) else
(
echo
0x%Nibble7%%Nibble6%%Nibble5%%Nibble4%%Nibble3%%Nibble2%%Nibble1%%Nibble0%
)
) else
(
echo
%Nibble7%%Nibble6%%Nibble5%%Nibble4%%Nibble3%%Nibble2%%Nibble1%%Nibble0%
)
goto
End
:Help
echo.
echo
%ScriptName%
[/p^|
/P] /hex ^<
number^>
echo
%ScriptName%
{/?
^|
-?
^|
/h^|
-h^|
/help
^|
-help}
echo.
echo
/hex Définit le ^<
number^>
qui doit être exprimé en hexadécimal.
echo
/p Définit que le préfixe doit être affiché en minuscules.
echo
/P Définit que le préfixe doit être affiché en majuscules.
echo.
/?
Affiche cette aide.
echo.
:End
if
not
"%~0
"==
":Help
" endlocal
exit
/b 0
Comme vous pouvez le constater, le script a vu sa taille réduite, les plus malins auront sûrement compris que la transformation en représentation hexadécimale peut se faire via une boucle, mais nous verrons cela au prochain chapitre. Les boucles auront, quand même, permis de condenser les opérations répétitives. La variable SInt32 a été remplacée par Nibble7 afin de traiter les divisions successives via une boucle for sans avoir à rajouter une ligne pour traiter le dernier quartet. Le script 62 peut facilement être utilisé dans un autre script grâce à la boucle for /f, comme avec le script 63 (remplacer <scriptname> par le nom donné au script 62).
2.
3.
4.
5.
6.
@
echo
off
for
/f "delims=
" %%A
in
('<
scriptname>
/p /hex -1') do
set
"Result=
%%A
"
echo
Résultat: %Result%
echo.
pause
exit
/b
Dans le script 63, l'option "delims=" est utilisée pour récupérer la sortie de la commande echo 0x%Nibble7%%Nibble6%… du script 62, car toutes les commandes echo se finissent par un retour à la ligne, le délimiteur doit donc être un retour à la ligne. Si plusieurs lignes doivent être récupérées en sortie d'un script, il est possible d'ajouter un compteur de ligne, comme dans le script 64.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
@
echo
off
setlocal
enabledelayedexpansion
rem Dans ce script, l'expansion retardée des variables est utilisée, elle sera cependant abordée au chapitre VI.
1>
"%cd%
\test.bat" echo
@
echo
off
1>>
"%cd%
\test.bat" echo
echo
Ligne 1
1>>
"%cd%
\test.bat" echo
echo
Ligne 2
1>>
"%cd%
\test.bat" echo
echo
Ligne 3
set
/a "Counter=
1"
for
/f "delims=
" %%A
in
('test.bat') do
(
set
"Result!Counter!
=
%%A
"
set
/a "Counter+=
1"
)
del
/Q test.bat
for
/l %%B
in
(1 1 !Counter!
) do
(
if
not
"!Result%%B!
"==
"" echo
Résultat %%B
:
!Result%%B!
)
pause
exit
/b
VI. L'expansion retardée des variables▲
Par défaut, l'expansion retardée des variables n'est pas activée, cependant il est possible de modifier ce comportement grâce à deux clés de registre contenant des valeurs REG_DWORD, à 0x1 si l'expansion retardée est activée et à 0x0 si l'expansion retardée est désactivée. Les entrées spécifiées dans la clé de l'utilisateur prennent le pas sur les entrées spécifiées dans la clé machine. Si l'expansion retardée est activée ou désactivée via les paramètres /v:on ou /v:off de la commande cmd, alors cette nouvelle configuration prend le pas sur la configuration du registre. Si par la suite, la commande setlocal est utilisée pour activer ou désactiver l'expansion retardée, alors les paramètres de la commande setlocal ont priorité sur les paramètres /v:on et /v:off.
HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\DelayedExpansion
HKEY_CURRENT_USER\Software\Microsoft\Command Processor\DelayedExpansion
Les variables ont une portée limitée au contexte dans lequel elles sont définies. Pour pouvoir utiliser des variables définies dans un contexte descendant, il faut utiliser l'expansion retardée. Celle-ci peut-être activée soit par défaut si les clés de registre appropriées ont été modifiées dans ce sens, ou alors avec la commande setlocal enabledelayedexpansion. Une fois que l'expansion retardée est activée, chaque variable définie sera accessible aussi bien dans la pile que dans le tas.
Ainsi, lorsque vous expanserez une variable dans un contexte ascendant, la variable prendra la dernière valeur qui lui a été attribuée comme spécifié au chapitre IVLa portée des variables : si l'expansion retardée est activée, à chaque création ou modification d'une variable, sa valeur est copiée à la fois dans le tas et sur la pile. L'expansion de ces variables se fait en utilisant le caractère ! (au lieu du caractère %) afin d'indiquer à l'interpréteur leurs emplacements.
Toutes les variables ayant été déclarées avant l'activation de l'expansion retardée ne sont accessibles que dans le contexte qui leur est propre ou dans un contexte descendant du contexte de création/modification.
VI-A. Cas de la boucle for▲
Dans une boucle for, la dernière exécution du corps de for est « fixée » dans le contexte d'appel de la boucle. Ainsi, dans le script 65, seule la dernière valeur est ajoutée à la variable Var.
2.
3.
4.
5.
6.
7.
@
echo
off
for
%%A
in
(a,z) do
(
set
"Var=
%Var%
%%A
"
)
echo
%Var%
pause
exit
/b 0
Si l'on modifie le script 65 en ajoutant l'expansion retardée, l'opération s'effectue sans erreur (script 66).
2.
3.
4.
5.
6.
7.
8.
@
echo
off
setlocal
enabledelayedexpansion
for
%%A
in
(a,z) do
(
set
"Var=
!Var!
%%A
"
)
echo
%Var%
pause
exit
/b 0
VI-B. Générer dynamiquement des noms de variables▲
Une autre application intéressante de l'expansion retardée des variables est de pouvoir créer des noms de variables en fonction de paramètres tel qu'un index numérique ou une chaîne de caractères. Des noms de variables ainsi construits permettent de créer une abstraction de tableau qu'il sera par la suite possible de parcourir rapidement avec une boucle for. Du fait de la syntaxe des paramètres de la boucle for, si l'on venait à placer le paramètre variable (l'index numérique ou la chaîne de caractères) entre des caractères %, le parsage de la ligne de commande générerait forcément une erreur. Par exemple avec un paramètre %%A ayant une valeur de 1, si l'on veut obtenir la variable ayant le nom Index1, il faudrait l'écrire %Index%%A%,mais l'interpréteur comprendrait qu'il y a une variable %Index% et une variable %A%. Si, avec le même exemple, on utilise l'expansion retardée, la variable s'écrirait !Index%%A!, ainsi plus d'erreur de parsage possible.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
@
echo
off
setlocal
enabledelayedexpansion
1>>
test.txt echo
Ligne1
1>>
test.txt echo
Ligne2
1>>
test.txt echo
Ligne3
set
/a "Counter=
1"
for
/f "delims=
" %%A
in
('type
test.txt') do
(
set
"Var!Counter!
=
%%A
"
set
/a "Counter+=
1"
)
del
/Q test.txt
for
/l %%B
in
(1 1 !Counter!
) do
if
not
"!Var%%B!
"==
"" echo
!Var%%B!
pause
exit
/b 0
Afin de rendre plus intuitive la lecture du script, il est possible de prendre des habitudes syntaxiques pour représenter de telles abstractions de tableau. Les caractères qui viennent tout de suite à l'esprit sont sûrement les caractères [ et ], ainsi une entrée de tableau pourrait s'écrire NomTableau[i] ou i est l'index numérique de position dans le tableau. Il suffit alors de parcourir le tableau indexé avec une boucle for /l ou de modifier directement la variable contenant la valeur à l'index voulu.
Les abstractions de tableaux littéraux peuvent être créées sur le même principe, ainsi une entrée de tableau serait accessible via l'identifiant NomTableau[NomEntrée]. Le parcours d'un tel tableau peut se faire avec une boucle for basique comme dans le script 68.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
@
echo
off
setlocal
enabledelayedexpansion
set
"Array[One]=
Entrée 1"
set
"Array[Two]=
Entrée 2"
set
"Array[Three]=
Entrée 3"
for
%%A
in
(One,Two,Three) do
echo
!Array[%%A]!
pause
exit
/b 0
Il faut savoir que l'interpréteur, par défaut, n'autorise que 8192 octets de données pour l'ensemble des valeurs des variables, mais autorise 64Ko de données pour l'ensemble des définitions de variables. Chaque définition de variable est composée du nom de la variable, du signe égal et de la valeur de la variable. Il reste donc 57344 octets (56Ko) pour placer les noms de variables et le signe égal, ce qui permet d'utiliser les noms de variables comme valeur significative et de récréer des structures de données ou des abstractions de tableaux.
VI-C. Parcourir une chaîne▲
L'expansion retardée permet une quantité d'actions sur les variables en modifiant l'évaluation syntaxique par l'interpréteur, la seule limite est la syntaxe. Par exemple, il est possible de vouloir connaître la taille d'une chaîne de caractères lorsqu'un script formate son affichage (ASCII Art, mini jeu en batch, etc.). Exemple avec le script 69.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
@
echo
off
setlocal
enabledelayedexpansion
set
"X=
xyz"
set
"Y=
"
echo
X:
call
:strlen
"%X%
"
call
:print
"%X%
" %ErrorLevel%
echo
Y:
call
:strlen
"%Y%
"
call
:print
"%Y%
" %ErrorLevel%
echo.
pause
exit
/b 0
:strlen
set
"str=
%~1
"
for
/l %%A
in
(0 1 4096) do
(
if
"!str:~%%A,1!
"==
"" exit
/b %%A
)
exit
/b 0
:print
if
%~2
GEQ
1 (
echo
La chaîne '%~1
' a une taille de %~2
caractère^(s^).
) else
(
echo
La chaîne est vide.
)
goto
:eof
VI-D. Mise en application de l'expansion retardée▲
Vous reconnaissez notre script témoin, celui-ci a encore été modifié pour le rendre plus efficient. La transformation en notation hexadécimale reprend le concept de génération dynamique de nom de variable. Dans le cas des divisions successives, l'utilisation de l'expansion retardée n'est pas nécessaire, car la commande set le gère automatiquement. De même, lors de l'expansion des variables NibbleX dans la commande echo, celles-ci n'ayant jamais été déclarées dans le contexte courant et l'expansion retardée étant activée depuis le début du script, elles sont donc implicitement adressées dans le tas. Nibble7 a été déclarée dans le contexte du script, cependant c'est toujours la dernière qui est traitée dans les boucles, donc ses valeurs successives sont toujours fixées dans le contexte du script.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
@
echo
off
set
"ScriptName=
%~nx0
"
set
"NegNum=
false"
set
/a "PrefixEnable=
0"
set
/a "UpperPrefix=
0"
:: La commande setlocal utilisée avec des paramètres ne créée pas de contexte local, mais modifie simplement celui-ci, d'où la double utilisation de setlocal.
setlocal
setlocal
enabledelayedexpansion
:ParseArgs
if
"%~1
"==
"" goto
Init
if
/i "%~1
"==
"/?
" goto
Help
if
/i "%~1
"==
"-?
" goto
Help
if
/i "%~1
"==
"/h" goto
Help
if
/i "%~1
"==
"-h" goto
Help
if
/i "%~1
"==
"/help
" goto
Help
if
/i "%~1
"==
"-help" goto
Help
if
/i "%~1
"==
"/hex" (
set
"Nibble7=
%~2
"
set
/a "Nibble7"
if
errorlevel
1 (
shift
/1
goto
BadSyntax
)
shift
/1
shift
/1
goto
ParseArgs
)
if
/i "%~1
"==
"/p" (
set
/a "PrefixEnable=
1"
if
%~1
EQU
/P set
/a "UpperPrefix=
1"
shift
/1
goto
ParseArgs
)
:BadSyntax
echo.
for
/f "delims=
" %%A
in
('net
helpmsg 87') do
echo
[ %~1
] %%A
echo.
if
not
"%~0
"==
":BadSyntax
" endlocal
exit
/b 1
:Init
if
defined
Nibble7 goto
Exec
:UnknowError
call
:BadSyntax
/hex
call
:Help
endlocal
exit
/b 2
:Exec
rem Si le nombre est négatif, on utilise son complément à un et on le définit comme négatif.
if
%Nibble7%
LSS
0 (
set
/a "Nibble7=
~Nibble7"
set
"NegNum=
true"
)
rem Divisions successives par 16
for
/l %%A
in
(0 1 6) do
set
/a "Nibble%%A
=
Nibble7 %% 16", "Nibble7/=
16"
rem Si le nombre est négatif, la valeur de chaque chiffre ^(Nibble^) est retranché à 15.
if
/i "%NegNum%
"==
"true" for
/l %%A
in
(0 1 7) do
set
/a "Nibble%%A
=
15 - Nibble%%A
"
rem Chaque chiffre ayant une valeur supérieure à 9 est transformé en sa représentation hexadécimale.
for
/l %%A
in
(0 1 7) do
(
if
!Nibble%%A!
EQU
10 set
"Nibble%%A
=
A"
if
!Nibble%%A!
EQU
11 set
"Nibble%%A
=
B"
if
!Nibble%%A!
EQU
12 set
"Nibble%%A
=
C"
if
!Nibble%%A!
EQU
13 set
"Nibble%%A
=
D"
if
!Nibble%%A!
EQU
14 set
"Nibble%%A
=
E"
if
!Nibble%%A!
EQU
15 set
"Nibble%%A
=
F"
)
if
%PrefixEnable%
EQU
1 (
if
%UpperPrefix%
EQU
1 (
echo
0X%Nibble7%%Nibble6%%Nibble5%%Nibble4%%Nibble3%%Nibble2%%Nibble1%%Nibble0%
) else
(
echo
0x%Nibble7%%Nibble6%%Nibble5%%Nibble4%%Nibble3%%Nibble2%%Nibble1%%Nibble0%
)
) else
(
echo
%Nibble7%%Nibble6%%Nibble5%%Nibble4%%Nibble3%%Nibble2%%Nibble1%%Nibble0%
)
goto
End
:Help
echo.
echo
%ScriptName%
[/p^|
/P] /hex ^<
number^>
echo
%ScriptName%
{/?
^|
-?
^|
/h^|
-h^|
/help
^|
-help}
echo.
echo
/hex Définit le ^<
number^>
qui doit être exprimé en hexadécimal.
echo
/p Définit que le préfixe doit être affiché en minuscules.
echo
/P Définit que le préfixe doit être affiché en majuscules.
echo.
/?
Affiche cette aide.
echo.
:End
if
not
"%~0
"==
":Help
" endlocal
exit
/b 0
VII. La gestion des flux▲
Depuis le début de ce document, nous avons utilisé des flux de données sans le savoir. Dans le cas d'un script batch, un flux de données est l'ensemble des données textuelles qu'une commande reçoit pour son exécution et qu'elle produit pendant cette même exécution. Ce flux de données transite via « le canal standard ».
VII-A. Le canal standard▲
Lors de l'exécution d'une commande, l'interpréteur alloue un espace de mémoire supplémentaire pour cette commande. Cet espace de mémoire est appelé « canal standard », il permet aux commandes exécutées de recevoir des données et de renvoyer des messages d'erreurs ou de réussites. Le canal standard a une entrée (aussi appelée « entrée standard » ou « STDIN »), une sortie (aussi appelée « sortie standard » ou « STDOUT ») et une troisième partie réservée aux messages d'erreurs (appelés « erreurs standards » ou « STDERR »). Le flux de données arrive dans l'entrée standard, il est traité par la commande puis un message de réponse est envoyé dans la sortie standard. Si une erreur survient pendant l'exécution de la commande, le message d'erreur (s'il y en a un) est placé dans l'erreur standard. Par défaut, dès qu'une donnée est placée dans la sortie standard ou dans l'erreur standard, elle est affichée par l'interpréteur. Chaque partie du canal standard est désignée par un numéro qui lui est propre, ce numéro est appelé « handle ». Il permet à l'interpréteur d'identifier la zone de mémoire réservée à chaque partie du canal. Notez qu'un handle désigne un fichier chargé en mémoire, l'interpréteur traite les constituants du canal standard comme des fichiers chargés en mémoire. Dans la suite de ce chapitre, nous utiliserons le terme « tampon » pour désigner un fichier chargé en mémoire afin de ne pas induire le lecteur en erreur. Le schéma ci-dessous représente le déroulement de l'exécution d'une commande.
VII-B. Les opérateurs de redirection▲
Afin de pouvoir contrôler les « mouvements » d'un flux, l'interpréteur fournit les opérateurs de redirection qui permettent, par exemple, d'envoyer des données dans un fichier ou de lire les données contenues dans un fichier. Le tableau ci-dessous donne une liste des différents opérateurs de redirection, leur syntaxe ainsi qu'une courte description.
Opérateur |
Syntaxe |
Description |
& |
Cmd1 & Cmd2 |
Cmd1 est exécuté puis, quel que soit son résultat, Cmd2 est exécuté. |
&& |
Cmd1 && Cmd2 |
Cmd1 est exécuté, puis si et seulement si Cmd1 ne produit pas d'erreur, Cmd2 est exécuté. |
| |
Cmd1 | Cmd2 |
Cmd1 est exécuté puis la sortie (STDOUT) de Cmd1 est envoyée dans l'entrée (STDIN) de Cmd2. |
|| |
Cmd1 || Cmd2 |
Cmd1 est exécuté puis, si et seulement si Cmd1 produit une erreur, Cmd2 est exécuté. |
< |
Cmd1 < File |
Le contenu du fichier (File) est envoyé dans l'entrée (STDIN) de Cmd1. |
> |
Cmd1 > File |
Cmd1 est exécuté, sa sortie (STDOUT) est envoyée dans un fichier(File). L'opération s'effectue en écriture seule, le fichier de destination n'est pas lu et cela a pour effet de le remplacer par un fichier ne contenant que les nouvelles données. |
Handle>File |
Le contenu du tampon désigné par l'Handle est copié dans le fichier (File) de destination. L'opération s'effectue en écriture seule, le fichier de destination n'est pas lu et cela a pour effet de le remplacer par un fichier ne contenant que les nouvelles données. |
|
>> |
Cmd1 >> File |
Cmd1 est exécuté, sa sortie (STDOUT) est envoyée dans un fichier (File). L'opération s'effectue en lecture et écriture, les données contenues dans le fichier de destination sont lues puis la sortie de Cmd1 est ajoutée à la fin. |
Handle>>File |
Le contenu du tampon désigné par l'Handle est copié dans le fichier (File) de destination. L'opération s'effectue en lecture et écriture, les données contenues dans le fichier de destination sont lues puis le contenu du tampon désigné par l'Handle est ajouté à la fin. |
|
<& |
<& Handle |
Redirige l'entrée standard (STDIN) dans le tampon désigné par l'Handle. |
Handle1 <& Handle2 |
Redirige le flux entrant dans le tampon désigné par l'Handle1 dans le tampon désigné par l'Handle2. |
|
>& |
>& Handle |
Redirige la sortie standard (STDOUT) dans le tampon désigné par l'Handle. |
Handle1>& Handle2 |
Redirige le flux sortant du tampon désigné par l'Handle1 dans le tampon désigné par l'Handle2. |
Tableau 14 : Opérateurs de redirection
VII-C. L'opérateur &▲
Cet opérateur permet d'exécuter plusieurs commandes, les unes à la suite des autres, sans tenir compte de ce qui s'est passé durant l'exécution de la commande précédente. Ainsi, chaque fois que l'interpréteur rencontre l'opérateur &, il sait qu'une autre commande est à exécuter, on parle d'exécution séquentielle.
2.
3.
4.
5.
@
echo
off &
cls
echo
Affichage:
echo.
&
echo.
&
echo
1 &
echo
2
echo.
&
echo.
pause
&
exit
/b 0
VII-D. La sortie standard▲
La sortie standard (à ne pas confondre avec l'affichage standard que l'on a vu au chapitre I) reçoit les messages d'une commande exécutée dans l'interpréteur. Le meilleur exemple d'utilisation de la sortie standard est la commande echo qui ne fait qu'envoyer les données qui lui sont passées en paramètres dans la sortie standard. Chaque fois qu'une chaîne de caractères est envoyée dans la sortie standard, l'interpréteur l'affiche aussitôt. Il est possible de rediriger la sortie standard vers un fichier ou dans l'entrée d'une autre commande. Si la sortie standard est redirigée, tout ce qui est envoyé dans la sortie standard est ensuite placé dans sa nouvelle destination et rien n'est affiché dans l'interpréteur. La sortie standard peut-être désignée par son handle (1) ou de façon implicite si le caractère > est utilisé dans l'opérateur de redirection. Les redirections peuvent être placées avant ou après la commande, cela ne change rien à leurs significations. Le schéma ci-dessous représente l'exécution d'une commande avec redirection de la sortie standard.
VII-E. Les opérateurs > et >>▲
Ces opérateurs permettent de rediriger la sortie standard ou le contenu d'un tampon vers un fichier. Seul le mode d'ouverture du fichier de destination diffère entre ces deux opérateurs. Ainsi, avec l'opérateur >, le fichier de destination n'est pas lu, les données sont donc effacées par l'écriture des nouvelles données. Avec l'opérateur >>, le fichier de destination est lu puis les nouvelles données sont ajoutées à la suite de celles déjà contenues dans le fichier de destination. Dans le script 72, la chaîne foo n'est pas dans le fichier Output.txt lorsqu'il est affiché par la commande type Output.txt, car le fichier n'a pas été lu lors de l'ajout de la chaîne bar.
2.
3.
4.
5.
6.
7.
8.
@
echo
off
echo
foo>
Output.txt
echo
bar>
Output.txt
echo
foobar>>
Output.txt
type
Output.txt
del
/Q Output.txt
pause
exit
/b 0
VII-F. L'erreur standard▲
L'erreur standard reçoit les messages d'erreurs d'une commande, ces informations sont des chaînes de caractères exposant, en langage humain, où se trouve l'erreur. Par défaut, l'interpréteur affiche les données contenues dans l'erreur standard à moins que le flux ne soit redirigé vers un fichier. L'handle de l'erreur standard est le 2 et elle n'est jamais utilisée de façon implicite, elle doit donc toujours être spécifiée dans la redirection. Il faut aussi noter que certaines commandes permutent les handles 1 et 2, cela est dû à de mauvaises pratiques de programmation.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
@
echo
off
rem La commande « set /a "Err=09" » génère toujours une erreur
2>
Log.txt set
/a "Err=
09"
echo
La commande a été exécutée.
echo.
type
Log.txt
del
/Q Log.txt
echo.
echo.
rem Dans le cas de la commande « change port /? » , le message est envoyé dans l'erreur standard alors qu'il n'y a pas d'erreur.
change
port /?
2>>
HelpChangePort.txt
echo
Syntaxe de change
port:
echo.
type
HelpChangePort.txt
del
/Q HelpChangePort.txt
echo.
echo.
pause
exit
VII-G. L'entrée standard▲
L'entrée standard est tout ce que la commande reçoit comme données, elle comprend la saisie au clavier de l'utilisateur (s'il y en a une) ainsi que le ou les fichiers qui seront envoyés par ce biais. L'handle de l'entrée standard est 0, mais elle peut aussi être utilisée de manière implicite si le caractère < est utilisé dans l'opérateur de redirection. Ci-dessous le schéma de l'exécution d'une commande avec redirection vers l'entrée standard.
Exemple avec le script 74 :
2.
3.
4.
5.
6.
7.
8.
@
echo
off
1>>
test.txt echo
foo
1>>
test.txt echo
bar
1>>
test.txt echo
foobar
<
test.txt more
del
/q test.txt
pause
exit
/b 0
L'entrée standard ne peut être redirigée directement vers un fichier sur le disque dur, les données doivent d'abord être redirigées vers un tampon avant de pouvoir être redirigées vers un fichier, c'est dû au fait que cela n'a théoriquement pas de sens. Exemple avec le script 75 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
@
echo
off
cls
rem Création d'un fichier texte
1>
test.txt echo
Ligne 1
1>>
test.txt echo
Ligne 2
rem Utilisation explicite de l'entrée standard
echo
Redirection 1:
0>>
LogInput.txt type
test.txt
echo
Output 1:
type
LogInput.txt
del
/Q LogInput.txt
echo.
rem L'handle 3 désigne un tampon défini par l'utilisateur.
echo
Redirection 2:
0<&
3>>
LogInput.txt type
test.txt
echo
Output 2:
type
LogInput.txt
del
/Q LogInput.txt
echo.
rem Utilisation implicite de l'entrée standard.
echo
Redirection 3:
<&
3>
logInput.txt type
test.txt
echo
Output 3:
type
LogInput.txt
del
/Q LogInput.txt
echo.
del
/Q test.txt
pause
exit
/b 0
Comme vous pouvez le voir dans l'affichage du script 75, lorsqu'on redirige l'entrée standard directement depuis l'handle 0 vers le fichier de sortie, rien ne se produit ; en effet, il n'est pas possible de rediriger l'entrée standard directement, il faut rediriger son contenu dans un autre tampon pour pouvoir le rediriger vers un fichier. Dans le deuxième et le troisième cas de notre exemple, l'entrée standard est redirigée dans le tampon désigné par l'handle 3 avant d'être redirigée vers le fichier, la commande type test.txt ne sert ici qu'à charger le fichier dans l'entrée standard.
VII-H. Le pseudo-périphérique NUL▲
Un pseudo-périphérique dénommé NUL est également disponible (si on le compare avec les systèmes de type Unix, ce serait le périphérique NUL, accessible via le point de montage /dev/nul). Il renvoie les données nulle part , celles-ci seront simplement supprimées du tampon source et seront donc définitivement perdues. Ce pseudo-périphérique trouve son utilité dans un certain nombre de cas comme lorsqu'on ne veut pas que la sortie d'une commande apparaisse dans l'affichage de l'interpréteur. Ce pseudo-périphérique n'a pas d'handle, mais un pseudo-fichier de périphérique afin d'être traité par l'interpréteur comme étant un fichier. Il n'a pas d'utilisation implicite et il ne devrait pas non plus être utilisé en entrée puisqu'il ne contient aucune donnée. Il peut donc être utilisé en sortie de tous les autres tampons. Il est utilisable via le pseudo-fichier nul, comme dans le script 76.
2.
3.
4.
5.
6.
7.
8.
@
echo
off
echo
test 1>
nul
set
/a "Var=
09" 2>
nul
set
/p "Var2=
Entrez une chaîne: " 0>
nul
echo.
echo
%Var2%
pause
exit
Dans le script 76, la commande set /p "Var2=Entrez une chaîne: " 0>nul se termine avant que l'utilisateur ne puisse entrer quoi que se soit, cela est dû à la redirection de l'entrée standard vers le pseudo-périphérique NUL. Ainsi lorsque Var2 est expansée, celle-ci est vide ; ce qui provoque l'affichage de la ligne Commande ECHO désactivée. Si l'on retire la commande echo. du script 76, on obtient l'affichage suivant :
La chaîne d'invite a bien été envoyée dans la sortie standard, mais comme set /p attend des données en provenance de l'entrée standard et que celle-ci est renvoyée vers le pseudo-périphérique NUL, le traitement de la commande est perdu et le retour à la ligne ne se fait jamais. Cela s'avère fort pratique lorsqu'il est nécessaire d'ajouter du texte dans un fichier sans retour à la ligne.
Le pseudo-fichier NUL est réservé par le système, il n'est donc pas possible de créer un fichier portant ce nom (même avec une extension de fichier).
VII-I. L'opérateur |▲
Cet opérateur, appelé « Pipe », permet de rediriger la sortie d'une commande dans l'entrée de la commande suivante, on parle de « pipe-line » de commande. Cependant, toutes les commandes n'ont pas la capacité de traiter les données contenues dans l'entrée standard ; les commandes qui peuvent faire cela sont appelées des filtres.
VII-I-1. Les filtres▲
Il existe plusieurs commandes qui agissent comme des filtres, c'est-à-dire qu'elles ont la capacité de récupérer le contenu de l'entrée standard, d'exécuter leurs traitements en fonction du contenu de l'entrée standard puis d'envoyer leurs messages de résultat dans la sortie standard afin d'être récupérés par un des différents moyens existants tels que les boucles for ou les redirections. Voici une liste non exhaustive des commandes pouvant être utilisées comme filtres.
Commandes |
Descriptions |
find |
Effectue une recherche de sous-chaînes dans les chaînes de l'entrée standard (chaque chaîne de l'entrée standard est séparée par un <CRLF>) puis renvoie les résultats dans la sortie standard. |
sort |
Effectue un tri par ordre alphabétique dans les chaînes de l'entrée standard (chaque chaîne de l'entrée standard est séparée par un <CRLF>) puis renvoie les résultats dans la sortie standard. |
more |
Récupère l'entrée standard puis envoie les données par paquets de tailles équivalentes à la taille de la fenêtre de l'interpréteur dans la sortie standard pour qu'ils soient affichés. |
findstr |
Effectue une recherche de sous-chaînes dans les chaînes de l'entrée standard (chaque chaîne de l'entrée standard est séparée par un <CRLF>) puis renvoie les résultats dans la sortie standard. |
Tableau 15 : Les filtres
N.B. : <CRLF> est un retour à la ligne, formaté Windows, d'une taille de deux octets.
Exemple avec le script 77.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
@
echo
off
(echo
foo &
echo
bar &
echo
foobar &
echo
test &
echo
retest &
echo
yes &
echo
no)>>
test.txt
echo
Affichage de sortie du filtre Sort
:
type
test.txt|
sort
echo.
echo
Affichage de sortie du filtre Find
:
type
test.txt|
find
"test"
echo.
echo
Affichage de sortie du filtre Findstr
:
type
test.txt|
findstr
test
echo.
pause
mode
con
cols=
80 lines=
5
type
test.txt|
more
echo.
del
/Q test.txt
pause
exit
/b 0
VII-I-2. Le filtre find▲
Find est une commande simple de recherche de chaînes, mais elle n'en est pas moins utile pour une recherche rapide et peu complexe de chaînes. Elle prend plusieurs paramètres tels que /i qui rend la recherche insensible à la casse, /n qui affiche les numéros des lignes trouvées, /c qui n'affiche que le nombre de lignes trouvées et /v qui ne recherche que les lignes ne contenant pas la chaîne.
Dans le script 78, le pipe doit être échappé afin de ne pas provoquer d'erreur. En effet, la transition de la commande du contexte du script vers celui de l'ensemble se fait sous forme de chaîne de caractères, il faut donc traiter la commande comme telle.
2.
3.
4.
5.
6.
7.
8.
@
echo
off
(echo
Con
tenu de la ligne 1 &
echo
Con
tenu de la ligne 2) 1>>
text.txt
for
/f "usebackq delims=
" %%A
in
(`type
text.txt^|
find
/n /v ""`) do
(
for
/f "tokens=
1-2 delims=
[]" %%a
in
("%%A
") do
echo
Ligne %%a
:
%%b
)
del
/Q text.txt
pause
exit
/b 0
VII-I-3. Le filtre sort▲
La commande sort effectue un tri par ordre alphabétique entre les chaînes qui lui sont passées en paramètres, ce qui permet de formater la sortie un peu comme un dictionnaire. Elle prend plusieurs paramètres tels que /r qui inverse l'ordre de tri, /o qui définit un fichier de sortie pour le tri, /t qui définit un répertoire temporaire pour l'exécution du tri, /l qui définit les paramètres régionaux pour l'exécution du tri ou encore /+n qui indique à partir de quel caractère commence le tri (ou n est l'index du premier caractère en partant de 1).
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
@
echo
off
1>
text.txt echo
zxy0
1>>
text.txt echo
abc9
1>>
text.txt echo
9abc
1>>
text.txt echo
0zxy
echo
Trie 1:
for
/f "delims=
" %%a
in
('type
text.txt^|
sort
/r') do
(
echo
%%a
)
echo.
&
echo
Trie 2:
for
/f "delims=
" %%a
in
('type
text.txt^|
sort
/+2') do
(
echo
%%a
)
del
/Q text.txt
echo.
pause
exit
/b 0
VII-I-4. Le filtre more▲
La commande more s'utilise généralement pour formater un affichage, mais elle peut aussi être utilisée dans le but de formater un lot de données dont le traitement nécessite une taille particulière ou de remplacer les tabulations par des espaces.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
@
echo
off
1>
test.txt echo
Tab1: "
1>>
test.txt echo
Tab2: "
1>>
test.txt echo
Tab3: "
echo
le texte:
type
test.txt
echo.
echo
la sortie:
for
/f "delims=
" %%A
in
('type
test.txt^|
more
/T1') do
(
echo
%%A
)
del
/Q test.txt
pause
exit
/b 0
VII-I-5. Le filtre findstr▲
La commande findstr permet des recherches de chaînes, mais à la différence de find, elle supporte des regexs. Même si celles-ci sont plutôt pauvres aux vues de celles dont disposent d'autres langages tels que Perl ou des regexs POSIX, elles n'en sont pas moins utiles pour traiter des chaînes. Voici une liste des motifs qu'elles supportent :
Motif |
Description |
. |
Caractère joker, correspond à une occurrence de tout caractère quel qu'il soit. |
* |
Répétition, zéro occurrence ou plus du caractère ou de la classe le précédent. |
^ |
Motif de début de ligne. |
$ |
Motif de fin de ligne. |
\x |
Utilisation littérale du métacaractère x. |
\<xyz |
Motif de début de mot. |
xyz\> |
Motif de fin de mot. |
[classe] |
Classe de caractères, correspond à tout caractère compris dans la classe. |
[^classe] |
Classe inverse, correspond à tout caractère non compris dans la classe. |
[x-y] |
Limites, correspond à tout caractère dans les limites spécifiées. |
Tableau 16 : Motifs des regexs de findstr
Vous aurez sûrement remarqué que certains de ces caractères sont significatifs pour l'interpréteur. En effet, les caractères ^<> doivent être échappés pour pouvoir être utilisés dans une regex de findstr, que ça soit dans un script ou dans l'interpréteur. Ainsi pour trouver la chaîne <?xml version="1.1" encoding="UTF-8" ?>, il est nécessaire d'utiliser la regex suivante :
^^^<?xml version="1\.[0-1]*" encoding="[0-9a-z\-]*" ?^>$
Il faut distinguer deux types d'échappement dans la regex précédente : les échappements inhérents à la ligne de commande et les échappements propres à la regex. Ainsi, les échappements de la ligne de commande sont ceux exposés au chapitre ILes échappements de caractères, soit <>|^&% ; et les échappements de la regex sont tous les métacaractères d'une regex, soit .*$\[]-. Ces caractères propres à la regex doivent être échappés avec le caractère \. Dans le cas des motifs de début et de fin de mot (<>), ces caractères doivent être échappés avec le caractère \ pour signifier à la regex qu'ils sont des métacaractères de la regex et non pas des caractères de la chaîne.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
@
echo
off
cls
(
echo
foo
echo
fao
echo
fa
echo
bar0
echo
bar1
echo
bar2
echo
bar3
echo
barfoo
echo
^<?
xml version=
"1.1" encoding=
"UTF-8" ?
^>
echo
^^*
echo
[MySection]
echo
C:\Windows\System32\cmd.exe
)>>
txt
echo
Con
tenu du fichier :
type
txt
echo.
echo.
echo
Début de chaîne(^^bar):
type
txt|
findstr
^^bar
echo.
echo
Fin de chaîne(1$):
type
txt|
findstr
1$
echo.
echo
Début de mot(\^<
fa):
type
txt|
findstr
\^<
fa
echo.
echo
Fin de mot(2\^>
):
type
txt|
findstr
2\^>
echo.
echo
Caractère unique(\^<
f.\^>
):
type
txt|
findstr
\^<
f.\^>
echo.
echo
Zéro ou plus doccurrences du caractère o(\^<
fo*
\^>
):
type
txt|
findstr
\^<
fo*
\^>
echo.
echo
Dans une classe de caractères(\^<
f[ao]o\^>
):
type
txt|
findstr
\^<
f[ao]o\^>
echo.
echo
Zéro ou plus d'occurrences de la classe de caractères[ao](\^<
f[ao]*
\^>
):
type
txt|
findstr
\^<
f[ao]*
\^>
echo.
echo
Intervalle de caractères dans une classe(\^<
bar[0-9]\^>
):
type
txt|
findstr
\^<
bar[0-9]\^>
echo.
echo
Hors de la classe de caractères(\^<
bar[^^12]\^>
):
type
txt|
findstr
\^<
bar[^^12]\^>
echo.
echo
Hors d'un intervalle de caractères dans une classe(\^<
bar[^^1-9]\^>
):
type
txt|
findstr
\^<
bar[^^1-9]\^>
echo.
echo
Zéro ou plus d'occurrences hors d'un intervalle de caractères dans une classe(\^<
bar[^^0-9]*
\^>
):
type
txt|
findstr
\^<
bar[^^0-9]*
\^>
echo.
echo
Déclaration XML ("^^^<?
xml version=
"1\.[0-1]*
" encoding=
"[0-9a-z\-]*
" ?
^>
$"):
type
txt|
findstr
"^^^<?
xml version=
"1\.[0-1]*
" encoding=
"[0-9a-z\-]*
" ?
^>
$"
echo.
echo
Métacaractère(\^<
[\^^\*
]*
\^>
):
type
txt|
findstr
\^<
[\^^\*
]*
\^>
echo.
echo
Section *
.ini(\^<
\[[a-z]*
\]\^>
):
type
txt|
findstr
\^<
\[[a-z0-9]*
\]\^>
echo.
echo
Chemin d'accès(\^<
[a-z:]*
\\[a-z]*
\\System32\\[a-z]*
\.[a-z]*
\^>
):
type
txt|
findstr
/i \^<
[a-z:]*
\\[a-z]*
\\System32\\[a-z]*
\.[a-z]*
\^>
echo.
del
/Q txt
pause
exit
/b 0
VII-J. L'opérateur <▲
Cet opérateur place le contenu d'un fichier dans l'entrée standard pour l'exécution d'une commande. Cette dernière doit être un filtre pour pouvoir traiter les données contenues dans l'entrée standard.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
@
echo
off
1>
text.txt echo
foo
1>>
text.txt echo
bar
1>>
text.txt echo
foobar
echo
Sort
:
echo.
sort
<
text.txt
echo.
echo.
echo
Findstr
:
echo.
<
text.txt findstr
/i \^<
foo
echo.
echo.
del
/Q text.txt
pause
exit
/b 0
VII-K. Le point sur « set /p » et « type »▲
set /p et type ne sont pas des filtres, même s'ils utilisent l'entrée standard, ils ne récupèrent pas son contenu. set /p utilise la sortie standard avant d'attendre des données en provenance de l'entrée standard ; cela a pour effet de supprimer toutes les données qui s'y trouvent. Il est donc inutile de tenter de se servir de set /p ainsi, car cela ne fonctionnera jamais. En ce qui concerne type, le contenu du fichier à afficher est placé dans l'entrée standard puis renvoyé dans la sortie standard, toutes les données présentes au moment du chargement du fichier dans l'entrée standard seront écrasées. Il n'est donc pas plus possible de tenter ce genre de chose avec type.
VII-L. Les opérateurs && et ||▲
Ces opérateurs permettent de gérer les erreurs dans un script. L'opérateur && permet d'exécuter une commande seulement si la commande précédente n'a pas provoqué d'erreur ; ainsi la seconde commande ne s'exécute qu'en cas de réussite de la première. Exemple avec le script 83 :
2.
3.
4.
5.
6.
7.
8.
9.
@
echo
off
2>
nul
set
"var=
foo" &&
2>
nul
set
"var2=
bar"
set
var
echo.
2>
nul
set
/a "nb=
09" &&
2>
nul
set
/a "nb2=
07"
set
nb
echo.
pause
exit
/b
L'opérateur || n'exécute la commande qui le suit seulement si la commande qui le précède provoque une erreur, ainsi la seconde commande ne s'exécute qu'en cas d'erreur de la première ; exemple avec le script 84.
2.
3.
4.
5.
6.
7.
@
echo
off
2>
nul
set
"var=
foo" ||
echo
Erreur de la commande :
set
"var=
foo"
echo.
2>
nul
set
/a "nb=
09" ||
echo
Erreur de la commande :
set
/a "nb=
09"
echo.
pause
exit
/b
VII-L-1. Tester les deux états d'erreurs▲
Il peut être nécessaire de tester si une commande s'est exécutée avec ou sans erreurs et effectuer des actions différentes dans chacun des deux cas. La combinaison des opérateurs && et || doit se faire selon un ordre précis afin de ne pas générer d'erreurs. En utilisant le test de réussite (&&) avant le test d'erreur (||), le test de réussite n'est valide que lorsqu'il n'y a aucune erreur dans la commande et le test d'erreur n'est valide que lorsqu'il y a une erreur au test de réussite . Exemple avec le script 85 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
@
echo
off &
cls
echo
Etat Méthodes Commandes
echo
====================================
rem Méthode 1
(
2>
nul
set
/a "var=
09"
) ||
(
echo
[Erreur] 1 set
/a "var=
09"
) &&
(
echo
[Ok] 1 set
/a "var=
09"
)
(
set
/a "var=
07"
) ||
(
echo
[Erreur] 1 set
/a "var=
07"
) &&
(
echo
[Ok] 1 set
/a "var=
07"
)
echo
====================================
rem Méthode 2
(
2>
nul
set
/a "var=
09"
) &&
(
echo
[Ok] 2 set
/a "var=
09"
) ||
(
echo
[Erreur] 2 set
/a "var=
09"
)
(
set
/a "var=
07"
) &&
(
echo
[Ok] 2 set
/a "var=
07"
) ||
(
echo
[Erreur] 2 set
/a "var=
07"
)
echo
====================================
echo.
pause
exit
/b 0
VII-M. Les opérateurs <& et >&▲
Ces opérateurs permettent de rediriger le flux d'un tampon à l'autre. L'opérateur <& redirige l'entrée standard ou le flux entrant dans un tampon vers un autre ; l'opérateur >& redirige la sortie standard ou le flux sortant d'un tampon vers un autre. Ces opérateurs ne peuvent être utilisés qu'avec des handles, toutes les redirections entre des tampons doivent se faire avec ces opérateurs.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
@
echo
off
cls
3>>
errlog.txt set
/a "var1=
09" 2>&
3
3>>
errlog.txt echo
foo 1>&
3
3>>
errlog.txt echo
bar 1<&
3
3>>
errlog.txt echo
foobar >&
3
echo
Fichier errlog.txt:
echo.
type
errlog.txt
echo.
del
/Q errlog.txt
pause
exit
/b 0
VII-N. Les handles pour tampon utilisateur▲
D'autres handles sont disponibles, ce sont les handles pour tampons utilisateurs. Ils sont utilisés afin de rediriger l'entrée standard et/ou de disposer d'un ou plusieurs tampons supplémentaires lors d'une exécution. Les handles pour tampons utilisateurs sont les handles 3 à 9 et l'utilisation qui est faite de ces derniers est définie par l'utilisateur. De même que pour les tampons du canal standard, si les données qu'ils contiennent ne sont pas redirigées vers un fichier, celles-ci seront affichées dans la fenêtre de l'interpréteur.
Un tampon n'est accessible que durant l'exécution qui l'a chargé en mémoire ; ainsi lorsqu'il est utilisé dans une ligne de commande, ce tampon n'existe que durant l'exécution de cette ligne de commande. Pour pouvoir utiliser l'handle d'un tampon utilisateur pendant l'exécution de plusieurs commandes, il faut que l'handle désigne toujours le même tampon; de plus, les handles sont assignés à des fichiers chargés en mémoire, il faut donc aussi un fichier à charger en mémoire pour qu'il puisse servir de tampon.
Les scripts batch ne gère nativement que les chaînes de caractères, le fichier qui servira de tampon doit donc être un fichier texte, il est créé au moment de la première utilisation et doit être supprimé manuellement, car une fois créé, il est enregistré sur le disque dur.
Dans un script batch, un bloc de commandes se symbolise avec des parenthèses () comme dans les conditions if où les commandes entre parenthèses ne sont exécuté que si la condition se vérifie ; ou encore dans les boucles for où le bloc de commandes entre parenthèses s'exécute à chaque itération de la boucle. Chaque fois que l'interpréteur rencontre une parenthèse ouvrante, il considère toutes les lignes de commandes jusqu'à la parenthèse fermante équivalente comme faisant partie d'un seul et même bloc indissociable. Les redirections agissent aussi sur ces blocs, il est ainsi possible de rediriger la sortie standard de chaque commande du bloc en plaçant une redirection avant ou après le bloc ; la redirection agira sur la totalité du bloc.
De la même manière, la redirection d'un tampon utilisateur peut agir sur un bloc de commandes ; le fait de rediriger un tampon utilisateur pour un bloc de commandes permet de lier le fichier texte et l'handle pour tout le bloc de commandes. Chaque fois qu'il sera fait référence à l'handle dans le bloc de commandes, l'handle désignera toujours le même fichier texte. Le fichier sera alors accessible via l'handle et via son nom. Cependant, durant l'exécution du bloc, le fichier ne peut ni être vidé ni être supprimé. Ainsi pour créer un bloc dans lequel l'handle 3 désigne le fichier buffer3, il faudrait utiliser la syntaxe suivante :
3>>
buffer3 (
:: Commande du bloc
)
Dans le script 87, des chaînes de caractères sont redirigées vers le tampon désigné par l'handle 3, le tampon est ensuite parcouru via le nom du fichier chargé dans le tampon désigné par l'handle 3. La commande 0>nul >&3 set /p "=xyz" sert à ajouter la chaîne xyz au tampon désigné par l'handle 3 sans retour à la ligne.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
@
echo
off
cls
3>>
buffer3 (
1<&
3 echo
Foo
1>&
3 echo
Bar
0>
nul
>&
3 set
/p "=
Foo"
>&
3 echo
Bar
for
/f "delims=
" %%a
in
('type
buffer3') do
(
0>
nul
>&
3 set
/p "=
%%a
"
)
>&
3 echo.
)
type
buffer3
del
/Q buffer3
pause
exit
/b 0
De la même façon, il est possible d'utiliser les redirections avec les blocs des conditions et des boucles ; exemple avec le script 88.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
@
echo
off
1>
text.txt echo
foo
1>>
text.txt echo
bar
1>>
text.txt echo
foobar
if
exist
text.txt 9>>
buffer9 (
>&
9 echo
Le fichier text.txt a été trouvé.
)
echo
Con
tenu de buffer9: &
type
buffer9 &
del
/Q buffer9 &
echo.
for
/f "delims=
" %%a
in
('type
text.txt') do
(((
echo
%%a
|
findstr
\^<
foo 1>&
3
echo
%%a
|
findstr
\^<
bar 1>&
4
echo
%%a
|
findstr
bar\^>
1>&
5
) 5>>
buffer5
) 4>>
buffer4
) 3>>
buffer3
echo
Con
tenu de buffer3: &
type
buffer3 &
del
/Q buffer3 &
echo.
echo
Con
tenu de buffer4: &
type
buffer4 &
del
/Q buffer4 &
echo.
echo
Con
tenu de buffer5: &
type
buffer5 &
del
/Q buffer5 &
echo.
del
/Q text.txt
pause
exit
VII-O. Les pseudo-fichiers de périphérique▲
Les pseudo-fichiers de périphériques sont des alias de « nom de fichier » qui permettent d'accéder au contenu d'un tampon de périphérique de la même manière qu'un fichier texte. Plusieurs périphériques sont disponibles depuis l'interpréteur, en voici la liste :
- le canal standard ;
- les ports séries ;
- les ports parallèles ;
- l'imprimante par défaut (seulement pour les systèmes Vista et inférieurs, et certains Windows 7) ;
- le périphérique auxiliaire.
VII-O-1. Le pseudo-fichier CON▲
Pour rappel le canal standard est le tampon utilisé par les commandes durant leurs exécutions, il est traité comme étant un périphérique du fait que l'entrée standard est liée par défaut au clavier et la sortie standard est liée par défaut à l'écran (la fenêtre de l'interpréteur). Le pseudo-fichier du canal standard est CON[:], il a assez peu d'applications concrètes, mais en voici une qui permet de saisir un texte sur plusieurs lignes à la différence de la commande set /p qui ne permet les saisies que sur une seule ligne.
2.
3.
4.
5.
6.
@
echo
off
for
/f "delims=
" %%a
in
('type
CON
') do
(
echo
%%a
)
pause
exit
N.B. Pour quitter la saisie et continuer l'exécution du script, il faut presser les touches Ctrl+Z et appuyer sur entrée. Voir l'annexe A.2A.2. Insertion de caractères de contrôle et de signaux pour plus de détails.
Le nom de fichier CON est réservé par le système, il n'est donc pas possible de créer un fichier qui porte ce nom. Les différentes constituantes du canal standard sont accessibles via le pseudo-fichier CON, ainsi l'entrée standard est accessible via CON[:]IN$, la sortie standard via CON[:]OUT$ et l'erreur standard via CON[:]ERR$.
VII-O-2. Les pseudo-fichiers COMx▲
Les ports séries utilisent les pseudo-fichiers COMx, où x est le numéro de port cible. Ce numéro peut aller de 1 à 256 même si un PC ne peut avoir 256 ports (ces numéros de ports sont attribuables à des ports mappés sur un réseau). L'utilisation des ports est plutôt simple, car il suffit de les lire ou les écrire via les commandes echo (pour l'écriture) et type ou more (pour la lecture). Il faut prendre en compte qu'un port série doit être configuré avec les mêmes paramètres que le port distant auquel il est connecté, cela peut être fait via la commande mode COMx ; l'affichage des paramètres se fait avec mode COMx /status. La configuration d'un port série ne seras pas abordée dans ce document du fait de la quantité d'éléments à prendre en compte. Vous pouvez effectuer des tests d'écritures/lectures sur un port série en utilisant une « loopback » entre deux ports séries d'un même ordinateur à l'aide d'un câble croisé 3, 5 ou 7 fils, il suffit alors de relier les deux ports séries avec le câble croisé. Les pseudo-fichiers COM1 à COM9 sont réservés par le système et les autres le seront si un port distant est mappé dessus. De même que pour le canal standard, les entrées et les sorties sont accessibles via COMx:IN$ et COMx:OUT$.
Écriture sur un port série, où <message> est la chaîne a envoyer par le port série.
echo
<
message>
>
COMx
Lecture depuis un port série.
type
COMx
more
<
COMx
VII-O-3. Les pseudo-fichiers LTPx▲
Les ports parallèles utilisent les pseudo-fichiers LTPx, où x est le numéro de port cible. Ce numéro peut aller de 1 à 256. Il existe un mappage local entre port COM et LTP via la commande mode LTPx[:]=COMy[:]. L'écriture et la lecture s'utilisent de la même façon que pour les ports séries, via les commandes echo et type ou more. Les pseudo-fichiers LTP1 à LTP9 sont réservés par le système et les autres le seront si un port distant est mappé dessus. De même que pour le canal standard, les entrées et les sorties sont accessibles via LTPx:IN$ et LTPx:OUT$.
VII-O-4. Le pseudo-fichier PRN▲
L'imprimante par défaut utilise le pseudo-fichier PRN et à la différence des autres pseudo-fichiers de périphérique, il n'est pas possible de lire les données qu'il contient. Ainsi seule l'écriture est possible sur ce pseudo-fichier de périphérique, cela s'effectue à l'aide d'une redirection de la sortie standard. Il faut noter que sous Windows 7 (Ultimate et Entreprise seulement) et Windows Serveur 2008 R2, ce pseudo-fichier de périphérique n'est plus fonctionnel. Le pseudo-fichier PRN est réservé par le système.
VII-O-5. Le pseudo-fichier AUX▲
Le périphérique auxiliaire est mappé sur le port COM1 pour des questions de compatibilité ascendante avec les anciens systèmes de la gamme Microsoft. Le mappage de ce port peut être modifié via la commande change port, voir l'aide en ligne de commande pour plus d'informations. Le pseudo-fichier AUX est réservé par le système. Si le pseudo-fichier AUX est mappé sur un autre pseudo-fichier qui possède plusieurs constituants (IN$, OUT$ ou ERR$), alors ces dernières sont accessibles via AUX:IN$, AUX:OUT$ et éventuellement AUX:ERR$.
VIII. Les fichiers de réponse▲
Un script a besoin de données pour s'exécuter, il arrive parfois que la quantité de données nécessaires soit très importante. Plutôt que d'utiliser une commande à rallonge, il est possible de placer ces données dans un fichier texte puis de les lire avec une boucle for. Cependant un fichier texte classique n'est guère adapté à ce type d'opération. La solution la plus pratique est l'utilisation de fichiers *.ini, ces derniers possèdent une syntaxe bien définie qui permet une lecture intuitive et donc facilite une éventuelle modification. Les fichiers *.ini qui doivent êtres utilisés par un script batch doivent être encodés avec le Code Page correspondant à la langue d'installation du système afin de permettre leur utilisation par un script.
VIII-A. La syntaxe des fichiers « *.ini »▲
Un fichier *.ini se compose de sections qui distinguent les différentes parties du fichier, chaque section relate les différentes entrées à utiliser pour une opération donnée. Les sections utilisent la syntaxe suivante, où <nom_de_la_section> est le nom que vous donnerez à votre section :
[<
nom_de_la_section>
]
Des commentaires peuvent être ajoutés afin de donner des informations facilitant la lecture du fichier. Pour insérer un commentaire, il faut utiliser le caractère ; en début de ligne. Exemple avec le fichier test.ini :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
; un en-tête de fichier sous forme de commentaire,
; celui-ci donne un court descriptif des entrées
; présentes dans le fichier ainsi que la fonction
; qu'il a et à quel script il est rattaché
; (éventuellement son auteur et les droits d'auteur).
[Section_One]
; un commentaire qui décrit la section [Section_One]
[Section_Two]
; un commentaire qui décrit la section [Section_Two]
Les entrées d'une section peuvent utiliser deux syntaxes, mais on utilise qu'un des deux types de syntaxes par section. Chacune de ces deux syntaxes est utilisée à des fins différentes en fonction des besoins.
La syntaxe des entrées « nommées » est utilisée lorsqu'une entrée est unique dans la section. Cependant, même si une entrée nommée ne peut être utilisée qu'une fois par section, elle est souvent utilisée dans chaque section du fichier. La syntaxe des entrées nommées est la suivante, où <Identifiant> est l'identifiant de l'entrée et <Value> est sa valeur :
<Identifiant>=<Value>
Les entrées « non nommées » quant à elles sont utilisées pour les opérations répétitives qui nécessitent plusieurs paramètres. Par exemple la copie de fichiers qui nécessite au moins un chemin d'accès source et un chemin d'accès de destination. Les entrées non nommées sont écrites avec les paramètres les uns à la suite des autres séparés par des virgules.
<Param1>,<Param2>,...,<ParamN>
VIII-B. Lecture d'un fichier « *.ini »▲
En général, on utilise un seul et même script par système pour lire les fichiers *.ini. Celui-ci est ensuite appelé par les scripts ayant besoin de lire un fichier *.ini. Un tel script pourrait ressembler au script 90.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
@
echo
off
setlocal
setlocal
enabledelayedexpansion
set
"ScriptName=
%~n0
"
set
"IniFile=
"
set
"IniSection=
"
set
"IniEntrie=
"
set
"CasseSensitive=
"
set
"CurrentSection=
"
:ParseArgs
if
"%~1
"==
"" goto
Init
if
/i "%~1
"==
"/?
" goto
Help
if
/i "%~1
"==
"-?
" goto
Help
if
/i "%~1
"==
"/h" goto
Help
if
/i "%~1
"==
"-h" goto
Help
if
/i "%~1
"==
"/help
" goto
Help
if
/i "%~1
"==
"-help" goto
Help
if
/i "%~1
"==
"/f" (
set
"IniFile=
%~2
"
shift
/1
shift
/1
goto
ParseArgs
)
if
/i "%~1
"==
"/s" (
set
"IniSection=
%~2
"
shift
/1
shift
/1
goto
ParseArgs
)
if
/i "%~1
"==
"/e" (
set
"IniEntrie=
%~2
"
shift
/1
shift
/1
goto
ParseArgs
)
if
/i "%~1
"==
"/i" (
set
"CasseSensitive=
/i"
shift
/1
goto
ParseArgs
)
:BadSyntax
for
/f "delims=
" %%a
in
('net
helpmsg 87') do
echo
[ %~1
] %%a
if
not
"%~0
"==
":BadSyntax
" endlocal
exit
/b 87
:Init
if
not
defined
IniFile (
call
:UnknowError
"/f"
exit
/b 87
)
if
not
exist
"%IniFile%
" (
call
:UnknowError
"%IniFile%
"
exit
/b 87
)
if
defined
IniSection goto
Exec
:UnknowError
if
"%~1
"==
"" (
call
:BadSyntax
"/s"
) else
(
call
:BadSyntax
"%~1
"
)
echo.
call
:Help
endlocal
exit
/b 87
:Exec
for
/f "usebackq eol=
; delims=
" %%a
in
(`type
"%IniFile%
"`) do
(
set
"FixLine=
%%~a
"
if
"!FixLine:~0,1!
"==
"[" (
for
/f "tokens=
1 delims=
[]" %%b
in
("%%a
") do
(
set
"CurrentSection=
%%~b
"
)
) else
(
if
%CasseSensitive%
"!CurrentSection!
"==
"%IniSection%
" (
if
defined
IniEntrie (
for
/f "tokens=
1 delims==
" %%b
in
("%%a
") do
(
if
%CasseSensitive%
"%%~b
"==
"%IniEntrie%
" echo
%%a
)
) else
(
echo
%%a
)
)
)
)
goto
End
:Help
echo.
echo
%ScriptName%
/f ^<
Ini_File_Path^>
/s ^<
Ini_Section^>
[/e ^<
Ini_Entrie^>
] [/i]
echo
%ScriptName%
/?
echo.
echo
/f Définit le fichier *
.ini à parser.
echo
/s Définit la section à renvoyer.
echo
/e Définit l'entrée à renvoyer.
echo
/i Définit que la recherche est insensible à la casse.
echo.
/?
Affiche cette aide.
echo.
:End
if
not
"%~0
"==
":Help
" endlocal
exit
/b 0
La lecture d'une section comprenant des entrées nommées peut se faire via un script semblable au script 91. Celui-ci lit le fichier stock.ini dans lequel est consigné l'inventaire d'un bar, ainsi lorsque vous demandez au script 91 une boisson, il vous dit s'il y en a et si c'est votre jour de chance. Remplacez ini_reader.bat par le nom que vous donnerez au script 90.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
@
echo
off
setlocal
enabledelayedexpansion
set
"Choix1=
Beer"
set
"Choix2=
Vodka"
set
/a "Addition=
0"
set
/a "NbConsommation=
0"
:Serveur
set
ChoixClient=
echo.
echo
Que voulez-vous boire ?
echo.
echo
1. Bière
echo
2. Vodka
echo
3. Rien pour l'instant, j'attends quelqu'un
echo
4. L'addition, s'il vous plaît ?
echo.
choice
/c 1234 /n
set
"ChoixClient=
%ErrorLevel%
"
echo.
if
%ChoixClient%
LSS
3 (
for
/f "delims=
" %%a
in
('ini_reader.bat /f stock.ini /s !Choix%ChoixClient%!
') do
set
"%%a
"
if
"!bar!
"==
"lucky" (
echo
C'est votre jour de chance, on a pas encore été livré, mais il nous en reste.
) else
(
echo
Pas de chance, on a pas encore été livré.
)
if
"!foo!
"==
"found" (
set
/a "Addition+=
!foobar!
"
set
/a "NbConsommation+=
1"
)
) else
if
%ChoixClient%
EQU
4 (
goto
CaisseEnregistreuse
)
timeout
/t 10 /nobreak
cls
goto
Serveur
:CaisseEnregistreuse
echo
Ça vous fera !Addition!
euros.
echo.
if
%NbConsommation%
GEQ
3 (
echo
Vous avez trop bu pour con
duire, vous devez me laisser vos clés.
echo
Écoutez, ne le prenez pas comme ça :
c'est la loi sinon je risque un procès.
echo
Moi je vends de l'alcool, je ne vous oblige pas à le boire...
)
pause
exit
/b 0
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
; état des stocks du BatchBar
; foo contient l'état du stock
; bar définit si c'est votre jour de chance
; foobar contient le prix de la boisson
[Beer]
; état des stocks de bière
foo
=
found
bar
=
lucky
foobar
=
2
[Vodka]
; état des stocks de vodka
foo
=
not found
bar
=
unlucky
foobar
=
5
; l'abus d'alcool est dangereux pour la santé
La lecture d'une section comprenant des entrées non nommées peut se faire via un script semblable au script 92. Celui-ci lit le fichier filelist.ini dans lequel sont consignés les fichiers à copier et ceux à supprimer. Remplacez ini_reader.bat par le nom que vous donnerez au script 90.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
@
echo
off
set
"ScriptName=
%~n0
"
echo
Copie des fichiers ...
for
/f "delims=
" %%a
in
('ini_reader.bat /f filelist.ini /s Copy
') do
(
for
/f "tokens=
1-3 delims=
," %%b
in
("%%a
") do
(
call
:CopyFunc
"%%b
" "%%c
" "%%d
"
)
)
pause
echo
Suppression des fichiers ...
for
/f "delims=
" %%a
in
('ini_reader.bat /f filelist.ini /s Delete') do
(
for
/f "tokens=
1-2 delims=
," %%b
in
("%%a
") do
(
call
:DeleteFunc
"%%b
" "%%c
"
)
)
pause
exit
/b 0
:CopyFunc
echo
%~3
copy
%1
%2
goto
:eof
:DeleteFunc
echo
%~2
del
/q %1
goto
:eof
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
; liste de fichiers à copier et à supprimer
; la section Copy contient les fichiers à copier
; la section Delete contient les fichiers à supprimer
[Copy]
; liste de fichiers à copier sous la forme <source>,<destination>,<print>
.\%ScriptName%.bat,.\%ScriptName%_Copie1.bat,Copie 1
du script ...
.\%ScriptName%.bat,.\%ScriptName%_Copie2.bat,Copie 2
du script ...
.\%ScriptName%.bat,.\%ScriptName%_Copie3.bat,Copie 3
du script ...
[Delete]
; liste de fichiers à supprimer sous la forme <file>,<print>
.\%ScriptName%_Copie1.bat,Suppression de la copie 1
du script ...
.\%ScriptName%_Copie2.bat,Suppression de la copie 2
du script ...
.\%ScriptName%_Copie3.bat,Suppression de la copie 3
du script ...
IX. Sécurité et script batch▲
La sécurité est un aspect important en informatique, les scripts batch n'échappent pas à la règle. En effet, les possibilités offertes par ce langage de script sont vastes, y compris pour quelqu'un de mal intentionné. Nous allons voir les différentes méthodes utilisables par un intrus et les solutions qu'il est possible d'y apporter. Cependant, les solutions exposées dans ce chapitre ne sont que des pistes de réflexion ; chaque problématique ayant des implications différentes, il ne peut y avoir de solution générique.
IX-A. Les variables▲
Contrairement à d'autres langages, les variables sont accessibles à tout le monde ou presque. Ainsi, n'importe quel programme peut accéder aux variables d'environnement d'un autre programme, du moment que celui-ci s'exécute en mode « utilisateur » et qu'il est lancé par le même utilisateur. Seuls les programmes qui sont exécutés par d'autres utilisateurs échappent à cette règle, à moins que le programme qui scrute les variables d'un autre soit lancé par un administrateur ou par le système (en tant qu'utilisateur). Pour cette partie du chapitre, il va nous falloir le programme ProcessExplorer de SysInternals disponible à cette URL : https://technet.microsoft.com/en-us/sysinternals/bb896653.aspx.
Une fois ProcessExplorer téléchargé, lancez-le en tant que simple utilisateur, il propose un affichage similaire à celui ci-dessous. Les programmes surlignés en rose ont été lancés par le système (en tant qu'utilisateur) et les programmes surlignés en bleu ont été lancés par l'utilisateur courant.
Dans ProcessExplorer, il est possible d'afficher la ligne de commande ayant appelé chaque programme (utile pour le débogage de scripts). Cela se fait via le menu View>Select Columns…, puis dans l'onglet Process Image cochez Command Line puis validez en cliquant sur Ok. Créez le script 93 et lancez-le, mais sans appuyer sur une touche au moment de la commande pause.
2.
3.
@
echo
off
set
"foo=
bar"
pause
Une fois le script lancé, allez dans ProcessExplorer puis cliquez sur le processus de votre batch (processus cmd.exe avec comme ligne de commande cmd /c "chemin\d'accès\de\votre\script"). Allez dans l'onglet Environment, vous pourrez y voir la variable foo et sa valeur.
De ce fait, les données sensibles ne devraient jamais être placées dans une variable, sans quoi vous risquez de créer une faille. La solution réside en partie dans les paramètres des boucles et les paramètres d'appel de fonction. Par exemple, dans le script 94, la chaîne bar n'est jamais accessible.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
@
echo
off
for
%%a
in
(bar) do
(
pause
echo
%%a
call
:Label
%%a
)
pause
exit
/b
:Label
pause
echo
%1
goto
:eof
Cependant, l'appel d'une commande externe ou d'un script avec comme paramètre la donnée sensible la rendra accessible à tout autre programme lancé par le même utilisateur via la commande d'appel. La meilleure solution reste la création d'un utilisateur spécifique à chaque opération sensible et d'appeler le script qui exécute l'opération via la commande runas. La donnée sensible pourra alors être placée dans le registre sous une clé du type HKCU\software\<mon_du_script>, dans ce cas la clé doit être créée par l'utilisateur spécifique à la tâche. Il convient également de modifier les autorisations de la clé (ACL), cela peut-être fait dans regedit via un clic-droit sur la clé puis sélectionnez Autorisations…. Il convient d'interdire toutes opérations, via l'option Refuser, à tout autre utilisateur et groupe sauf l'utilisateur spécifique. Les autorisations du registre peuvent également être modifiées via la commande regini, entrez regini /? dans l'interpréteur de commandes pour plus d'informations. Par la suite, le contenu de la clé pourra être récupéré via la commande reg query ; de préférence dans une boucle for. Exemple avec le script 95 : il est nécessaire d'ajouter le fichier ipmi.reg au registre afin que le script 95 fonctionne.
2.
3.
4.
5.
6.
7.
8.
@
echo
off
for
/f "usebackq skip=
2 delims=
" %%a
in
(`reg
query
"HKCU\software\ipmi\key" /v con
nect`) do
(
for
/f "tokens=
3" %%b
in
("%%a
") do
echo
%%b
)
pause
exit
/b 0
2.
3.
4.
5.
6.
Windows Registry Editor Version 5
.00
[HKEY_CURRENT_USER\Software\ipmi]
[HKEY_CURRENT_USER\Software\ipmi\key]
"connect"="0123456789
"
Un script de ce type pourra alors être appelé avec le script suivant (où <specific_user_name> est le nom de l'utilisateur spécifique et <cmd_script> est la commande d'appel du script 95).
2.
3.
4.
@
echo
off
runas
/User:<
specific_user_name>
"<
cmd_script>
"
pause
exit
/b 0
Il convient de modifier également les autorisations (ACL) du script qui effectue la tâche sensible afin d'éviter toute réécriture inappropriée du script, cela peut être fait via un clic-droit sur le fichier, sélectionnez Propriétés puis l'onglet Sécurité. Il convient de modifier les autorisations de la même manière que pour la clé registre. Les autorisations d'un fichier peuvent aussi être modifiées via la commande icacls, entrez icacls /? dans l'interpréteur de commandes pour plus d'informations. Une commande cacls existe aussi, mais elle est à proscrire, car considérée comme obsolète, son support n'est pas garanti à l'avenir.
IX-B. L'injection de commandes▲
Comme tout langage de script, le batch est sensible à « l'injection ». Cette méthode consiste à insérer des commandes lors d'une entrée (saisie utilisateur ou lecture d'un fichier texte). Cette section est donc consacrée à l'application de la célèbre maxime « Never trust in user input ».
La commande set /p n'est pas sensible à l'injection de commandes, cependant la variable définie par la commande set /p peut contenir des redirections qui injecteront la (ou les) commande(s) lors de son expansion. Exemple avec le script 97 :
2.
3.
4.
5.
6.
7.
8.
@
echo
off
set
/p foo=
Entrez une chaîne :
echo.
echo
Vous avez entré: %foo%
echo.
pause
Si l'utilisateur saisit la chaîne bar & echo foobar, on obtient l'affichage suivant dans lequel on peut voir l'exécution de la commande echo foobar.
La solution est simple et repose sur l'échappement, mais dans une forme que nous n'avons pas encore vue. Il s'agit de l'échappement de chaînes dans leur ensemble via les guillemets, ainsi toute chaîne placée entre guillemets peut comporter n'importe quels caractères spéciaux sans que cela ne pose de problème. Si l'on reprend le script 97 et qu'on le modifie de la manière suivante (script 98), on obtient un script sûr.
2.
3.
4.
5.
6.
7.
8.
@
echo
off
set
/p foo=
Entrez une chaîne :
echo.
echo
Vous avez entré :
"%foo%
"
echo.
pause
Pour en revenir à ce qui avait été dit au chapitre IILa commande « set » sur l'initialisation de variable, il est possible d'utiliser la syntaxe de la commande set pour pouvoir travailler avec une variable qui risque de comporter une injection en plaçant les guillemets dans la variable. Cependant la variable contiendra toujours les guillemets. Exemple avec le script 99 :
2.
3.
4.
5.
6.
7.
8.
9.
@
echo
off
set
/p foo=
Entrez une chaîne :
set
foo=
"%foo%
"
echo.
echo
Vous avez entré :
%foo%
echo.
pause
Il est aussi possible d'utiliser la syntaxe des conditions pour tester si la saisie comporte un caractère qui pourrait poser problème et sortir du processus sans autres formes de procès. Exemple avec le script 100 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
@
echo
off
setlocal
enabledelayedexpansion
set
"ScriptName=
%~dpnx0
"
set
/p foo=
Entrez une chaîne:
for
/l %%a
in
(0 1 4096) do
(
if
"!foo:~%%a,1!
"==
"&
" (
call
:Injection
"!foo:~%%a,1!
" "%foo%
"
) else
if
"!foo:~%%a,1!
"==
"|
" (
call
:Injection
"!foo:~%%a,1!
" "%foo%
"
) else
if
"!foo:~%%a,1!
"==
"<
" (
call
:Injection
"!foo:~%%a,1!
" "%foo%
"
) else
if
"!foo:~%%a,1!
"==
">
" (
call
:Injection
"!foo:~%%a,1!
" "%foo%
"
)
)
echo.
echo
Vous avez entré :
%foo%
echo.
pause
exit
/b 0
:Injection
1>>
log.txt echo
Le caractère %1
a été détecté dans une saisie utilisateur dans le script "%ScriptName%
" exécuté par l'utilisateur "%UserName%
". La saisie était %2
.
exit
Dans le script 100, un message a été écrit dans un log ; cela aurait tout aussi pu être l'envoi d'un mail à l'administrateur, la prise d'une photo avec la webcam ou toute autre solution jugée nécessaire.
De même que lors d'une saisie utilisateur, la lecture d'un fichier texte peut poser problème. La commande type et la boucle for ne sont pas sensibles à l'injection. Cependant, l'utilisation ultérieure des données lues dans le fichier peut permettre l'injection. Exemple avec le script 101 et le fichier test_injection.txt :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
@
echo
off
setlocal
enabledelayedexpansion
for
/f "delims=
" %%a
in
(test_injection.txt) do
(
set
str=
%%a
echo
Dans la boucle: !str!
call
:Label
"!str!
"
)
pause
exit
/b 0
:Label
echo
Après un appel: %~1
goto
:eof
2.
foo & echo foobar
bar
On remarque que l'utilisation de la variable !str! dans la boucle ne pose pas de problème tandis qu'après l'appel du :Label, elle provoque l'injection. C'est dû au fait que la chaîne est passée au contexte du label par copie et que dans le contexte du corps de for, l'échappement se fait automatiquement. Une nouvelle chaîne étant créée pour le contexte du label, celui-ci ne considère pas la chaîne comme comportant un échappement. Si un échappement est ajouté dans le fichier texte, cela ne change rien, et ce quel qu'en soit le nombre. La seule solution est de tout traiter dans le corps de for lorsqu'une entrée provient d'un fichier texte, d'utiliser les guillemets lors d'une commande d'appel ayant en paramètre une entrée provenant d'un fichier texte et éventuellement de tester la présence de caractère de redirection (seulement s'il n'y a pas de légitimité à ces caractères).
Annexe A. Les caractères▲
Cette annexe présente des informations complémentaires sur l'utilisation des caractères.
A.1. Affichage des caractères de contrôle▲
Ces caractères graphiques sont utilisés pour afficher les points de code normalement attribués à des caractères de contrôles. Ces caractères sont applicables à tous les codes pages.
Point de code décimal |
Point de code hexadécimal |
Point de code Unicode |
Caractères |
Description |
0 |
0x00 |
U+0020 |
' ' |
Caractère espace. |
1 |
0x01 |
U+263A |
☺ |
Émoticône face souriante transparente. |
2 |
0x02 |
U+263B |
☻ |
Émoticône face souriante pleine. |
3 |
0x03 |
U+2665 |
♥ |
Symbole cœur plein. |
4 |
0x04 |
U+2666 |
♦ |
Symbole carreau plein. |
5 |
0x05 |
U+2663 |
♣ |
Symbole trèfle plein. |
6 |
0x06 |
U+2660 |
♠ |
Symbole pique plein. |
7 |
0x07 |
U+2022 |
|
Symbole Puce. |
8 |
0x08 |
U+25D8 |
◘ |
Symbole rectangle plein avec puce évidée. |
9 |
0x09 |
U+25CB |
○ |
Symbole cercle. |
10 |
0x0A |
U+25D9 |
◙ |
Symbole rectangle plein avec cercle évidé. |
11 |
0x0B |
U+2642 |
♂ |
Symbole mâle. |
12 |
0x0C |
U+2640 |
♀ |
Symbole femelle. |
13 |
0x0D |
U+266A |
♪ |
Symbole note de musique noire. |
14 |
0x0E |
U+266B |
♫ |
Symbole note de musique double noire. |
15 |
0x0F |
U+263C |
☼ |
Symbole soleil. |
16 |
0x10 |
U+25BA |
► |
Symbole triangle plein vers la droite. |
17 |
0x11 |
U+25C4 |
◄ |
Symbole triangle plein vers la gauche. |
18 |
0x12 |
U+2195 |
↕ |
Symbole flèche verticale avec double pointe (vers le haut et le bas). |
19 |
0x13 |
U+203C |
‼ |
Double point d'exclamation. |
20 |
0x14 |
U+00B6 |
¶ |
Symbole paragraphe alternatif. |
21 |
0x15 |
U+00A7 |
§ |
Symbole paragraphe. |
22 |
0x16 |
U+25AC |
▬ |
Symbole rectangle plein horizontal. |
23 |
0x17 |
U+21A8 |
↨ |
Symbole flèche verticale avec pointe vers le haut et base en bas. |
24 |
0x18 |
U+2191 |
↑ |
Symbole flèche verticale vers le haut. |
25 |
0x19 |
U+2193 |
↓ |
Symbole flèche verticale vers le bas. |
26 |
0x1A |
U+2192 |
-> |
Symbole flèche horizontale vers la droite. |
27 |
0x1B |
U+2190 |
← |
Symbole flèche horizontale vers la gauche. |
28 |
0x1C |
U+221F |
∟ |
Symbole coin inférieur gauche. |
29 |
0x1D |
U+2194 |
↔ |
Symbole flèche horizontale avec double pointe (vers la gauche et la droite). |
30 |
0x1E |
U+25B2 |
▲ |
Symbole triangle plein vers le haut. |
31 |
0x1F |
U+25BC |
▼ |
Symbole triangle plein vers le bas. |
127 |
0x7F |
U+2302 |
⌂ |
Symbole maison. |
A.2. Insertion de caractères de contrôle et de signaux▲
L'interpréteur autorise l'insertion de caractères de contrôle via le clavier à différentes fins. Des signaux peuvent aussi être envoyés à l'interpréteur pour permettre une interaction complète avec l'utilisateur. Les caractères sont indiqués entre <> et les signaux sont préfixés par #.
Caractère / Signal |
Raccourci clavier |
Description |
<SOH> |
CTRL-A |
Insertion du caractère SOH |
<STX> |
CTRL-B |
Insertion du caractère STX |
#SIGINT |
CTRL-C |
Émission du signal #SIGINT (sortie forcée du contexte courant) |
<EOT> |
CTRL-D |
Insertion du caractère EOT |
<ENQ> |
CTRL-E |
Insertion du caractère ENQ |
<ACK> |
CTRL-F |
Insertion du caractère ACK |
<BEL> |
CTRL-G |
Insertion du caractère BEL (émission d'un bip sonore par la carte mère via le buzzer) |
<BS> |
CTRL-H |
Insertion du caractère BS (décrémente la position du curseur, limité à la ligne courante, voir exemple d'utilisation du caractère BS) |
<TAB> |
CTRL-I |
Insertion du caractère TAB (insertion d'une tabulation) |
N.C. |
CTRL-J |
N.C. |
<VT> |
CTRL-K |
Insertion du caractère VT |
<DEL> |
CTRL-L |
Insertion du caractère DEL |
<CRLF> |
CTRL-M |
Insertion des caractères CR et LF (insertion d'un retour à la ligne formaté Windows) |
<SO> |
CTRL-N |
Insertion du caractère SO |
<SI> |
CTRL-O |
Insertion du caractère SI |
<DLE> |
CTRL-P |
Insertion du caractère DLE |
<DC1> |
CTRL-Q |
Insertion du caractère DC1 |
<DC2> |
CTRL-R |
Insertion du caractère DC2 |
<SUB> |
CTRL-S |
Insertion du caractère SUB |
<DC4> |
CTRL-T |
Insertion du caractère DC4 |
<NAK> |
CTRL-U |
Insertion du caractère NAK |
<SYN> |
CTRL-V |
Insertion du caractère SYN |
<ETB> |
CTRL-W |
Insertion du caractère ETB |
<CAN> |
CTRL-X |
Insertion du caractère CAN |
<EM> |
CTRL-Y |
Insertion du caractère EM |
#SIGTERM |
CTRL-Z |
Émission du signal #SIGTERM (sortie normale du contexte courant) |
<NUL> |
CTRL-@ |
Insertion du caractère NULL. |
<ESC> |
CTRL-^ |
Insertion du caractère ESC |
<GS> |
CTRL-$ |
Insertion du caractère GS |
<FS> |
CTRL-* |
Insertion du caractère FS |
#SIGBREAK |
CTRL-PAUSE |
Émission du signal #SIGINT dans le processus exécuté en tâche de fond. |