IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Apprendre la programmation de script batch

Ce cours a pour but de vous apprendre la programmation de script batch. Tombés en désuétude, ils reviennent forts depuis Windows Server 2012 du fait que celui-ci utilise une interface en ligne de commande aussi appelée CLI (Command Line Interface) ou CUI (Console User Interface).

4 commentaires Donner une note à l´article (5)

Article lu   fois.

L'auteur

Profil Pro

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

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++.

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.

Script 1
Sélectionnez
1.
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.

Script_Hello_World

É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>.

STD_Prompt

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.

Image non disponible

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.

Script 2
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
cls
rem Remarque 1
:: Commentaire 1
@echo off
rem Remarque 2
:: Commentaire 2
pause
Image non disponible

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.

Script 3
Sélectionnez
1.
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.

Script 4
Sélectionnez
1.
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.

Script 5
Sélectionnez
1.
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.

Script 6
Sélectionnez
1.
2.
3.
4.
@echo off
echo foo ^
bar
pause
Image non disponible

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.

Script 7
Sélectionnez
1.
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.

Escape_str_in_echo_cmd

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.

Script 8
Sélectionnez
1.
2.
3.
@echo off
echo %PATH%
pause

Ce qui produirait un affichage similaire à celui ci-dessous.

PATH

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
%UserProfile%\AppData\Roaming.

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 :
%ProgramFiles(x86)%\Common Files.

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
%SystemDrive%\Windows.

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.

Script 9
Sélectionnez
1.
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
Variable_Quote

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

Script 10
Sélectionnez
1.
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
Variable_Parse_String_0

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

Script 11
Sélectionnez
1.
2.
3.
4.
5.
@echo off
set "X=string"
echo Substitution avec astérisque: %X:*t=d%
echo.
pause
Variable_Parse_String_1

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.

Script 12
Sélectionnez
1.
2.
3.
4.
5.
6.
@echo off
set "VAR=ma chaîne"
echo %VAR%
set "VAR=%VAR:chaîne=voiture%"
echo %VAR%
pause
Variable_Parse_String_2.2

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

Script 13
Sélectionnez
1.
2.
3.
4.
5.
6.
@echo off
set "VAR=ma chaîne"
echo %VAR:~0,2%
set "VAR=%VAR:~3,6%"
echo %VAR%
pause
Image non disponible

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.

Script 14
Sélectionnez
1.
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
Variable_Parse_String_2

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.

Script 15
Sélectionnez
@echo off
set /a "VAR1=1", "VAR2=2"
echo VAR1: %VAR1%
echo VAR2: %VAR2%
pause
Image non disponible

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.

Script 16
Sélectionnez
@echo off
set /a "VAR1=1"
set /a "VAR2=VAR1"
echo VAR1: %VAR1%
echo VAR2: %VAR2%
pause
Set_a_var_unexpand

Si un nom de variable inexistante ou indéfinie est utilisé, alors elle prend la valeur 0.

Script 17
Sélectionnez
1.
2.
3.
4.
5.
@echo off
rem La variable VAR1 n'existe pas.
set /a "VAR2=VAR1"
echo VAR2: %VAR2%
pause
Image non disponible

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.

Script 18
Sélectionnez
1.
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
Parse_Error

Une autre méthode qui fonctionne pour ce cas est la suivante.

Script 19
Sélectionnez
1.
2.
3.
4.
5.
@echo off
set "SInt32=-2147483648"
set /a "SInt32"
echo %SInt32%
pause
TransTypage

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"

Modulo_0

Dans un script, par contre, c'est l'inverse.

Script 20
Sélectionnez
1.
2.
3.
4.
5.
6.
@echo off
set /a "Mod=5 % 2"
echo Mod1: %Mod%
set /a "Mod=5 %% 2"
echo Mod2: %Mod%
pause
Modulo_1

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.

Script 21
Sélectionnez
1.
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
Image non disponible

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.

Script 22
Sélectionnez
1.
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
Variable_Attribution

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

Script 23
Sélectionnez
@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
Variable_AlgBool

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.

Script 24
Sélectionnez
1.
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
Unaire

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.

Script 25
Sélectionnez
1.
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
Hexa_0

Le résultat du script 25 nous montre que le calcul s'effectue correctement, de même avec le script 26.

Script 26
Sélectionnez
1.
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
Hexa_1

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.

Script 27
Sélectionnez
1.
2.
3.
4.
@echo off
set /a "Hexa=0x80000000"
echo Résultat:    %Hexa%
pause
Hexa_2

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.

Script 28
Sélectionnez
1.
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
Variable_In_tSign_Hex

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.

Script 29
Sélectionnez
1.
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
Variable_Int_Sign_2_HexDecForm

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.

Script 30
Sélectionnez
@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
Octal_0

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.

Script 31
Sélectionnez
@echo off
echo Nombre 08:
set /a "Octal1=08"
echo.
echo Nombre 09:
set /a "Octal1=09"
echo.
pause
Octal_1

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 <condition> <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.

Syntaxe des conditions if et else avec parenthèses
Sélectionnez
if <condition> (
    <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 <condition1> <cmd1> [else if <condition2> <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.

Syntaxe des conditions if, else if et else
Sélectionnez
if <condition1> (
    <commande1>
    <commande2>
) else if <condition2> (
    <commande3>
    <commande4>
) else if <condition3> (
    <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 <condition1> <cmd1> else if <condition2> <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 <condition1> <cmd1> else if /i <condition2> <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.

Script 32
Sélectionnez
1.
2.
3.
@echo off
if exist "%cd%" echo %cd% existe bien.
pause
If_Exist

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>

Script 33
Sélectionnez
1.
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
If_Defined

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>

Script 34
Sélectionnez
1.
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
If_Errolevel

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>

Script 35
Sélectionnez
1.
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
If_CmdExtVersion

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

Script 36
Sélectionnez
1.
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
If_op1_comp_op2

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.

Script 37
Sélectionnez
1.
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
If_str1_comp_str2

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.

Script 38
Sélectionnez
1.
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>

Script 39
Sélectionnez
1.
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
Goto

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.

Script 40
Sélectionnez
1.
2.
3.
4.
5.
@echo off
:LabelUn
call :LabelDeux
:LabelDeux
goto :LabelUn
Dep_Stack

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.

Script 41
Sélectionnez
1.
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 :

Script 42
Sélectionnez
1.
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
Call_Args

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 :

Script 43
Sélectionnez
1.
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
Modif_Call_Args

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

Script 44
Sélectionnez
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
Shift_Call_Args

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.

Script 45
Sélectionnez
1.
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
Exit

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

Script 46
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
@echo off

echo %ErrorLevel%
call :UnLabel
echo %ErrorLevel%
pause
exit /b
:UnLabel
exit /b 1
output_code

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 :

Script 47
Sélectionnez
1.
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.

Script 48
Sélectionnez
1.
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.

Syntaxe de la commande start
Sélectionnez
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.

Script 49
Sélectionnez
1.
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 :

start_0

Seconde instance :

start_1

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.

Script 50
Sélectionnez
1.
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 :

start_i_0

Seconde instance :

start_i_1

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.

Script 51
Sélectionnez
1.
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
start_b

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 :

Script 52
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
@echo off
setlocal
set "X=foo"
echo %X%
endlocal
echo %X%
pause
exit
setlocal_endlocal

IV-L. Mise en application des contextes

Script 53
Sélectionnez
1.
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 :

Script 54
Sélectionnez
1.
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
For

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.

Script 55
Sélectionnez
1.
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
For_d_r

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>

Script 56
Sélectionnez
1.
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
For_Counter

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.

Script 57
Sélectionnez
1.
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 »

Script 58
Sélectionnez
1.
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
For_Find

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.

Script 59
Sélectionnez
1.
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.

Script 60
Sélectionnez
1.
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.

Script 61
Sélectionnez
1.
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

Script 62
Sélectionnez
1.
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).

Script 63
Sélectionnez
1.
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
For_Call_Script

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.

Script 64
Sélectionnez
1.
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
For_Call_Script_Multiline

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.

Clés registres relatives à l'expansion retardée des variables
Sélectionnez
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.

Script 65
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
@echo off
for %%A in (a,z) do (
    set "Var=%Var% %%A"
)
echo %Var%
pause
exit /b 0
Expansion_For_0

Si l'on modifie le script 65 en ajoutant l'expansion retardée, l'opération s'effectue sans erreur (script 66).

Script 66
Sélectionnez
1.
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
Expansion_For_1

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.

Script 67
Sélectionnez
1.
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
Expansion_Dynamic_Var

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.

Script 68
Sélectionnez
1.
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
Expansion_Literal_Array

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.

Script 69
Sélectionnez
1.
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
Expansion_strlen

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.

Script 70
Sélectionnez
1.
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.

STD_Chanel

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.

Script 71
Sélectionnez
1.
2.
3.
4.
5.
@echo off & cls
echo Affichage:
echo. & echo. & echo 1 & echo 2
echo. &  echo.
pause & exit /b 0
Operateur_AND

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.

STD_Out

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.

Script 72
Sélectionnez
1.
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
Image non disponible

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.

Script 73
Sélectionnez
1.
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
STDERR

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.

Image non disponible

Exemple avec le script 74 :

Script 74
Sélectionnez
1.
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
Redirection_stdin

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 :

Script 75
Sélectionnez
1.
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
Redirection_stdin_file

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.

Script 76
Sélectionnez
1.
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
Redirection_NUL_0

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 :

Redirection_NUL_1

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.

Script 77
Sélectionnez
1.
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
Filtre_1
Filtre_2
Filtre_3

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.

Script 78
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
@echo off
(echo Contenu de la ligne 1 & echo Contenu 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
Filtre_Find

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).

Script 79
Sélectionnez
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
Filtre_Sort

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.

Script 80
Sélectionnez
1.
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
Filtre_More

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.

Script 81
Sélectionnez
1.
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 Contenu 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 d’occurrences 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
Regex

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.

Script 82
Sélectionnez
1.
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
Operateur_stdin

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 :

Script 83
Sélectionnez
1.
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
Operateur_ANDAND

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.

Script 84
Sélectionnez
1.
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
OperateurPIPEPIPE

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 :

Script 85
Sélectionnez
1.
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
Test_Erreur

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.

Script 86
Sélectionnez
1.
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
Operateur_SHIFTAND

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 :

Syntaxe de la redirection de bloc
Sélectionnez
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.

Script 87
Sélectionnez
1.
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
Tampon_Utilisateurs

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.

Script 88
Sélectionnez
1.
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 Contenu 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 Contenu de buffer3: & type buffer3 & del /Q buffer3 & echo.
echo Contenu de buffer4: & type buffer4 & del /Q buffer4 & echo.
echo Contenu de buffer5: & type buffer5 & del /Q buffer5 & echo.
del /Q text.txt
pause
exit
Tampon_Utilisateur_bloc_if_for

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.

Script 89
Sélectionnez
1.
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.

Pseudo-fichier_CON

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

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

test.ini
Sélectionnez
1.
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.

Script 90
Sélectionnez
1.
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.

Script 91
Sélectionnez
1.
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 conduire, 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
stock.ini
Sélectionnez
1.
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.

Script 92
Sélectionnez
1.
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
filelist.ini
Sélectionnez
1.
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.

ProceXp

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.

Script 93
Sélectionnez
1.
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.

ProceXp_Evironment

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.

Script 94
Sélectionnez
1.
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.

Script 95
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
@echo off

for /f "usebackq skip=2 delims=" %%a in (`reg query "HKCU\software\ipmi\key" /v connect`) do (
    for /f "tokens=3" %%b in ("%%a") do echo %%b
)

pause
exit /b 0
ipmi.reg
Sélectionnez
1.
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).

Script 96
Sélectionnez
1.
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 :

Script 97
Sélectionnez
1.
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.

Injection_cmd

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.

Script 98
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
@echo off

set /p foo=Entrez une chaîne : 
echo.
echo Vous avez entré : "%foo%"
echo.

pause
Escap_injection_cmd

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 :

Script 99
Sélectionnez
1.
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 :

Script 100
Sélectionnez
1.
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 :

Script 101
Sélectionnez
1.
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
test_injection.txt
Sélectionnez
1.
2.
foo & echo foobar
bar
Injection_txt

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
graphiques

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.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2015 Juillard Jean-Baptiste. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.