Les fichiers batch sont des scripts de commandes qui
s'exécutent dans l'interpréteur de commande Windows. Alors quelle
pourrait être l'utilité d'écrire ces lignes de commande dans un script ?
En fait, il y a plusieurs raisons :
- éviter d'avoir à réécrire sans cesse les mêmes commandes lors de tâches répétitives ;
- possibilité de créer de vrais petits « programmes »
facilitant les tâches qui doivent être réalisées via l'interpréteur de
commande.
Nous allons étudier les méthodes permettant de concevoir ces
programmes. Dans la suite de ce document, nous utiliserons la convention
suivante :
- les termes « interpréteur de commande » ou « interpréteur » désignent l'exécutable « cmd.exe » ;
- dans les syntaxes de commande, les parties encadrées avec les caractères [ et ] sont optionnelles, les parties encadrées avec les caractères < et >
sont à remplacer par différentes informations à fournir (celles-ci
seront précisées avec la syntaxe) et les parties encadrées avec les
caractères { et } sont des parties à choix multiple où chaque choix est séparé des autres avec le caractère |.
La convention syntaxique est la même que celle pour l'aide en
ligne de commande, ainsi il vous sera plus facile de la comprendre.
I-A. L'éditeur de texte▲
Un simple éditeur de texte, tel que le
notepad
fourni avec Windows, s'avère peu pratique pour écrire des scripts
batch. Pour exploiter pleinement les capacités de ces scripts, il faut
pouvoir encoder les caractères non anglophones comme les accents ou le
« ç » afin de pouvoir accéder aux fichiers dont les noms comportent ce
genre de caractères exotiques. La plupart des éditeurs de texte encodent
les fichiers avec le jeu de caractères de la norme ANSI qui correspond à
la langue d'installation du système d'exploitation alors que
l'interpréteur de commande utilise une étendue de page de code (aussi
appelée
Code Page) comme CP-850 qui est l'encodage par défaut de l'interpréteur pour les systèmes Windows installés en français (France).
Sous Windows, il y a un éditeur de texte que vous possédez
peut-être déjà, c'est Notepad++ (la version 5.9.x ou supérieure est
nécessaire) ; si ce n'est pas le cas, vous pouvez le télécharger ici :
http://notepad-plus-plus.org/fr/
Dans ce dernier, vous pourrez sélectionner l'encodage dans le
menu « Encodage > Codage de caractères », puis si vous voulez le
français par exemple : sélectionnez « Langues d'Europe
occidentale>OEM-850 ». Dans Notepad++, les
code pages
sont appelés OEM-XXX, au lieu de CP-XXX, mais ce sont les mêmes
encodages. Il faut aussi noter que dans le même menu que OEM-850, il y a
OEM-863 : français ; c'est bien du français, mais pour le Québec. Cette
manipulation sera à effectuer pour chaque script afin d'utiliser le bon
encodage, il n'est pas possible d'effectuer cette opération de façon
automatique dans Notepad++.
I-B. Encodage des caractères▲
La gestion des pages de code dans l'interpréteur se fait via les commandes
mode et
chcp, ces commandes permettent d'afficher le
code page utilisé ou de le modifier en utilisant l'une des syntaxes suivantes.
Définir le
code page à utiliser (où
<XXX> est le numéro de
code page).
mode
con
cp select=
<
XXX>
chcp
<
XXX>
Afficher le
code page utilisé.
mode
con
cp [/status]
chcp
Le tableau 1 fournit une liste non exhaustive des différents
code pages utilisés par l'interpréteur de commande.
Code Page |
Description |
CP-437 |
pour le support des langues anglophones. |
CP-720 |
pour le support des langues arabes. |
CP-737 |
pour le support du grec. |
CP-775 |
pour le support des langues baltes. |
CP-850 |
pour le support des langues d'Europe occidentale (mis à
jour par CP 858) dont le français (France), mais aussi l'allemand, le
basque, le catalan, le danois, l'espagnol, le finnois, l'italien, le
néerlandais, le norvégien, le portugais, le suédois, l'afrikaans, le
faeroese, le flamand et l'irlandais. |
CP-852 |
pour le support des langues d'Europe centrale. |
CP-855 |
pour le support de l'alphabet cyrillique. |
CP-857 |
pour le support du turc. |
CP-858 |
pour le support des langues d'Europe occidentale, dont
le français (France), il s'agit d'une mise à jour de 1998 basée sur
CP-850 ou seul le symbole « € » a été ajouté au point de code 0xD5. |
CP-861 |
pour le support de l'islandais. |
CP-863 |
pour le support du français (Québec). |
CP-864 |
pour le support des langues arabes. |
CP-866 |
pour le support de l'alphabet cyrillique. |
CP-869 |
pour le support du grec. |
Tableau 1 : Code Page
Il faut noter que malgré la mise à jour de CP-850 par CP-858,
l'encodage par défaut pour l'interpréteur sous Windows installé en
français (France) reste CP-850. Il est donc préférable d'utiliser
CP-850. La liste des
code pages supportés par un système Windows est disponible dans le registre sous la clé :
HKLM\SYSTEM\CurrentControlSet\Control\Nls\CodePage. Vous noterez la présence de
1252 dans la clé précédemment mentionnée, il s'agit de l'encodage
Windows-1252
utilisé par les API Windows et donc par la plupart des blocs-notes sur
les systèmes installés en français (France) ; cependant il n'est pas
recommandé de l'utiliser dans l'interpréteur de commande du fait que les
commandes ne sont pas faites pour le supporter, il peut même provoquer
des bogues dans certains cas. L'encodage par défaut pour l'interpréteur
peut être trouvé via la valeur
OEMCP de type
REG_SZ toujours dans la clé précédemment mentionnée.
Il est possible d'ajouter les
Code Pages manquants (par défaut, seuls les
code pages
correspondant à la langue d'installation sont disponibles) en
installant le pack de langue correspondant à l'encodage voulu. Chaque
pack de langue comprend une table de conversion permettant à Windows de
convertir un point de code en sa représentation graphique et donc de
l'afficher. Il est aussi possible d'utiliser l'Unicode (UCS-2 seulement)
avec les sorties de commandes internes de l'interpréteur pour permettre
l'interaction avec les programmes qui utilisent Unicode de manière
« native » (pour Perl par exemple) en appelant l'interpréteur via la
commande suivante (où
<commande> est la commande à exécuter).
cmd
/u <
commande>
Dans Notepad++, les caractères absents du clavier peuvent
êtres rajoutés via le menu « Édition>Panneau des caractères ASCII »
puis double-clic sur le caractère voulu.
I-C. Hello World▲
Nous allons commencer par le traditionnel « hello world »,
dont voici le code (Script 1). Copiez le code dans Notepad++, puis
enregistrez-le avec l'encodage OEM-850 et l'extension « .bat » ou
« .cmd » (seulement sur les systèmes Vista et supérieur).
La différence entre fichier « .bat » et fichier « .cmd »
tient dans l'interpréteur de commande, en effet sur les systèmes Windows
XP, il y avait deux interpréteurs de commande : « cmd.exe » et
« COMMAND.COM ». Les fichiers avec l'extension « .bat » étaient exécutés
par « cmd.exe » et les fichiers avec l'extension « .cmd » étaient
exécutés par « COMMAND.COM », les deux types de fichiers avaient des
spécificités différentes. Cependant sur les systèmes Windows Vista et
supérieur, seul subsiste « cmd.exe », tous les scripts « .bat » et
« .cmd » sont exécutés par « cmd.exe » et ont les mêmes spécificités.
1.
2.
3.
4.
5.
6.
7.
8.
9.
@
echo
off
cls
echo
Hello World !!!! Déjà là ?
echo.
pause
Lorsqu'on exécute ce script en cliquant dessus, on obtient l'affichage suivant.
Étudions la composition du script 1. La première ligne,
@echo off, est déjà intéressante, elle est composée :
- du préfixe @ qui sert à inverser l'état de l'affichage standard ;
- de la commande echo qui sert à gérer l'affichage en ligne de commande ;
- et du paramètre off qui sert à désactiver l'affichage standard.
L'affichage standard définit ce que l'interpréteur de
commande affiche par défaut. Par exemple lors du lancement de
l'interpréteur de commande ci-dessous ; l'affichage standard renvoie le
chemin d'accès du répertoire courant, soit
C:\Users\Portable>.
Lors de l'exécution d'un script, l'affichage standard
renvoie, par défaut, le chemin d'accès du répertoire courant suivi de la
commande en cours d'exécution comme dans l'exemple ci-dessous.
Le préfixe
@, quand il est
placé en début de ligne, sert à inverser l'état de l'affichage standard
(activé ou désactivé) pour l'exécution de la commande qui le suit (pas
seulement pour la commande
echo). Ce comportement prend fin une fois la commande exécutée. Ainsi la commande
@cd /d "C:\Users\Portable\" ne sera affichée que si l'affichage standard est désactivé. La syntaxe du préfixe
@ est la suivante (où
<commande> est la commande à exécuter) :
@
<
commande>
La commande
echo gère l'affichage dans l'interpréteur, elle peut :
- modifier l'état de l'affichage standard ;
- afficher l'état de l'affichage standard ;
- afficher un message ou une ligne vide dans l'interpréteur.
Désactiver l'affichage standard peut être fait via la syntaxe suivante (seuls les erreurs et les messages de la commande
echo sont affichés).
echo
off
Activer l'affichage standard peut être fait via la syntaxe suivante (tout est affiché).
echo
on
Utilisée sans paramètre, la commande
echo renvoie l'état de l'affichage standard en cours.
echo
Si l'on reprend le script 1, la ligne
@echo off permet de désactiver l'affichage standard sans que la commande soit affichée. Sur la deuxième ligne du script 1, la commande
cls
est utilisée pour vider la fenêtre de l'interpréteur de son contenu,
cette commande ne prend aucun paramètre. Sa syntaxe est donc la
suivante.
cls
La ligne suivante du script 1 est vide, elle ne sera donc pas
prise en compte lors de l'exécution du script permettant ainsi de le
rendre plus lisible. La quatrième ligne est composée de la commande
rem
et d'une chaîne de caractères, cette commande permet d'insérer des
remarques dans son script. Si et seulement si l'affichage standard est
activé, la commande
rem sera affichée. La syntaxe de la commande
rem est la suivante (où
<remarque> est la chaîne de caractères à insérer en remarque).
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).
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.
1.
2.
3.
4.
5.
6.
7.
cls
@
echo
off
pause
Comme vous le voyez dans l'affichage du script 2, la commande
rem Remarque 1, est présente à l'écran ; l'affichage standard étant activé, les commandes exécutées sont toutes affichées. La chaîne
:: Commentaire 1 n'est pas affichée, c'est dû au fait que le préfixe
:: n'est pas une commande et donc n'est pas renvoyé par l'affichage standard. Vient ensuite la commande
@echo off qui désactive l'affichage standard sans que la commande ne soit affichée, suivie de la commande
rem Remarque 2 qui n'est pas affichée (l'affichage standard étant à présent désactivé), pas plus que la chaîne
:: Commentaire 2 qui est de toute façon exclue par l'affichage standard.
I-E. Les échappements de caractères▲
Les caractères spéciaux (aussi appelé méta-caractères) sont
des caractères ayant une signification particulière pour l'interpréteur
de commande. Chaque fois que l'interpréteur les rencontre, il leur
applique le traitement correspondant, quelles que soient leurs positions
dans la ligne de commande. En voici la liste :
&
|
^ <
>
( )
Pour pouvoir les utiliser dans une chaîne de caractères, il faut les « échapper » avec le caractère
^,
c'est-à-dire placer ce caractère devant. Ainsi l'interpréteur saura
qu'ils font partie d'une chaîne et qu'ils n'ont rien à voir avec la
commande. Voir le script 3 comme exemple.
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.
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.
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.
1.
2.
3.
4.
@
echo
off
echo
foo ^
bar
pause
D'autres échappements existent, cependant ils seront abordés
plus loin dans ce document, car ils s'appliquent à des commandes
précises et ne concernent pas les autres. Par exemple, la commande
echo qui considère le point comme un échappement de la chaîne qui le suit. Lorsque la commande
echo
est directement suivie d'un point, elle considère la chaîne qui se
trouve après le point comme une chaîne à afficher, retour à la ligne
compris. Cela permet d'utiliser en début de chaîne un « mot » qu'elle
aurait, dans le cas contraire, considéré comme un paramètre. Exemple
avec le script 7.
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.
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 : & | ^ < > ( ) %.
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.
1.
2.
3.
@
echo
off
echo
%PATH%
pause
Ce qui produirait un affichage similaire à celui ci-dessous.
Les identifiants de variable ne sont pas sensibles à la casse,
c'est-à-dire que l'interpréteur ne fait pas la différence entre
majuscule et minuscule lorsqu'il expanse, crée ou modifie une variable.
Ainsi les identifiants
PATH,
Path et
path désignent une seule et même variable.
II-A. Les variables d'environnement▲
Nous allons commencer par les variables d'environnement,
elles sont toutes de type chaîne de caractères. C'est le système
d'exploitation qui définit leurs valeurs soit parce que d'un système à
l'autre leurs valeurs peuvent être différentes, soit parce que ces
variables sont définies dynamiquement lors de leur expansion. Cela
permet d'accéder à des fichiers dont le chemin d'accès pourrait ne pas
être le même d'une machine à l'autre ou de pouvoir obtenir, par exemple,
l'heure ou la date dont les valeurs sont modifiées en permanence.
Dans le tableau 3, vous trouverez la liste de ces variables.
Dans la première colonne, vous trouverez le nom de ces variables ; ces
noms sont réservés par le système, et même s'il est possible de modifier
leur valeur pendant l'exécution du script, les modifications apportées
prendront fin en même temps que le script. De plus, dans certains cas,
la modification de leur valeur peut entraîner une mauvaise évaluation de
l'environnement par le script. La deuxième colonne donne la description
de la valeur renvoyée par la variable. Et enfin, la troisième colonne
donne la compatibilité en fonction de la version du système. Dans cette
colonne, il est fait mention de « systèmes NT », il s'agit de la
« famille » du système d'exploitation. Les systèmes NT sont des systèmes
reposant sur un noyau de type NT provenant du système Windows du même
nom. Cette famille comprend au jour où sont écrites ces lignes :
- les systèmes Windows NT X.Y (où X est la révision majeure et Y la révision mineure) ;
- les systèmes Windows 2000 ;
- les systèmes Windows XP ;
- les systèmes Windows Server 2003 ;
- les systèmes Windows Vista ;
- les systèmes Windows Server 2008 ;
- les systèmes Windows 7 ;
- les systèmes Windows Server 2012 ;
- et les systèmes Windows 8.
Variable |
Description |
Compatibilité |
AllUsersProfile |
Renvoie le chemin d'accès complet jusqu'au répertoire des données utilisateurs communes à tous les utilisateurs. Par défaut : %HomeDrive%\ProgramData. |
Disponible sur tous les systèmes NT. |
AppData |
Renvoie le répertoire commun des données d'application sous la forme
%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.
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"
set
/p VAR_Saisie_Un=
ma saisie un:
set
/p "VAR_Saisie_Deux=
ma saisie deux: "
set
/a VAR_Calcul_Un=
1+10
set
/a VAR_Calcul_Deux"=
2+20"
set
/a "VAR_Calcul_Trois=
3+30"
echo.
echo
%VAR_Espace_Un%
echo
%VAR_Espace_Deux%
echo
%VAR_Espace_Trois%
echo.
set
VAR_Saisie
echo.
set
VAR_Calcul
echo.
pause
Comme vous pouvez le constater, les guillemets occupent une
place prépondérante dans ce script ; observez bien où ils sont placés.
De la ligne 2 à la ligne 4, les valeurs des variables sont des chaînes
de caractères avec des espaces. La déclaration de la variable
VAR_Espace_Un se fait avec des guillemets placés de part et d'autre de la chaîne, la déclaration de la variable
VAR_Espace_Deux
se fait avec des guillemets : un placé avant le signe égal et l'autre à
la fin de la chaîne ; la déclaration de la troisième variable
VAR_Espace_Trois
se fait avec des guillemets : un placé avant le nom de la variable et
l'autre à la fin de la ligne. Si on se reporte à l'affichage obtenu, on
remarque que la première et la troisième variables affichent une sortie
correcte ; la deuxième variable quant à elle ne fonctionne pas ; on
déduit donc facilement que si les guillemets s'ouvrent avant
l'identifiant de la variable et se ferment après la valeur de la
variable, ils ne font pas partie de la variable ; et si les guillemets
s'ouvrent avant la valeur de la variable et se ferment après la valeur
de la variable, ils font partie de la variable.
Les deux saisies de l'utilisateur donnent aussi un résultat intéressant, la ligne de commande qui déclare
VAR_Saisie_Un n'utilise pas de guillemets ; par contre la ligne de commande qui déclare
VAR_Saisie_Deux,
elle, en utilise. Lors de l'affichage des invites de saisie
utilisateur, les espaces se trouvant avant les chaînes d'invites ont
disparu, et ceux se trouvant après sont affichés. On en déduit que les
espaces en début de ligne sont ignorés et que ceux de la fin sont
considérés comme faisant partie de la chaîne d'invite.
En ce qui concerne les variables
VAR_Calcul_XXX,
elles donnent toutes un résultat valide cependant il est toujours
préférable de respecter les règles de syntaxe ; c'est-à-dire toujours
mettre l'expression numérique entre guillemets. Cela sera utile dans la
suite du chapitre.
II-C. Les variables de type chaîne de caractères▲
Ce sont, à mon sens, les plus utilisées en batch. Ce sont
toutes les variables composées de caractères alphanumériques et autres
caractères. Les chaînes de caractères sont déclarées via la commande
set
et il n'est pas nécessaire d'utiliser des guillemets pour que les
espaces soient pris en compte comme faisant partie de la chaîne, et ce,
où qu'ils soient placés dans la chaîne.
Elles offrent aussi d'autres possibilités comme la
substitution de chaîne dans l'expansion de la variable. Ainsi avec la
syntaxe suivante, il est possible de modifier la variable lors de son
expansion sans que cela n'altère sa valeur (où
<variable> est l'identifiant de la variable,
<old_string> est la chaîne à remplacer et
<new_string> est la chaîne à insérer à la place de
<old_string>). Exemple avec le script 10.
%<variable>:<old_string>=<new_string>%
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
<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.
1.
2.
3.
4.
5.
@
echo
off
set
"X=
string"
echo
Substitution avec astérisque: %X:*t=d%
echo.
pause
Il ne faut jamais utiliser un astérisque seul dans la
substitution, sans quoi, l'interpréteur de commande se fermera sans
autres formes de procès. L'astérisque en fin de chaîne ne provoque pas
d'erreur, mais ne permet pas la substitution, la variable est expansée
avec sa valeur d'origine.
La substitution ne modifie pas la valeur de la variable, elle
transforme juste la variable au moment de son expansion. Pour modifier
la valeur de la variable, il faut le faire avec la commande
set. Exemple avec le script 12.
1.
2.
3.
4.
5.
6.
@
echo
off
set
"VAR=
ma chaîne"
echo
%VAR%
set
"VAR=
%VAR:chaîne=voiture%
"
echo
%VAR%
pause
Vous pouvez aussi ne développer qu'une partie d'une chaîne en
sélectionnant les caractères voulus. Cela se fait via la syntaxe
suivante (où
<variable> est l'identifiant de la variable,
<index> est l'index du premier caractère en partant de 0 et
<longueur> est la longueur de la sélection). Exemple avec le script 13.
%<variable>:~<index>,<longueur>%
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
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.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
@
echo
off
set
"VAR=
ma chaîne"
echo
%VAR:~-6%
echo
%VAR:~0,-7%
pause
II-D. Les variables de type nombre entier signé▲
La commande
set /a permet
d'utiliser les nombres entiers signés allant de -2 147 483 648 à
+2 147 483 647 (codés sur 32bits). Si vous n'utilisez pas le paramètre
/a
dans la commande, alors la séquence de chiffres sera considérée comme
étant une chaîne de caractères, et non un nombre. La syntaxe de la
commande
set /a est la suivante.
set
/a <
expression>
<expression> utilise, quant à elle, la syntaxe suivante.
["]<
identifiant>
[<
attribution>
[{<
immédiate>
|
<
sous-expression>
}]]["]
Les expressions numériques doivent être placées entre des
guillemets si elles contiennent des opérateurs logiques ou de groupement
(détaillé plus loin dans ce chapitre). Ces différentes constituantes
sont :
- <identifiant> qui est l'identifiant de la variable de destination ;
- <attribution> qui
est l'opérateur d'attribution de l'expression, comme le signe égal qui
effectue l'opération d'attribuer une valeur à une variable, d'autres
opérateurs d'attribution seront détaillés dans cette section ;
- puis soit <immédiate> qui est une valeur immédiate (un nombre quelconque), soit <sous-expression> qui est une sous-expression, ces dernières seront détaillées dans le reste de cette section.
Vous pouvez spécifier plusieurs expressions en les séparant par des virgules.
@
echo
off
set
/a "VAR1=
1", "VAR2=
2"
echo
VAR1: %VAR1%
echo
VAR2: %VAR2%
pause
Les chaînes de caractères présentes dans l'expression sont
considérées comme des variables et sont expansées (à l'exception de
l'identifiant de la variable de destination), cela permet de faire des
opérations sur des variables sans avoir à utiliser de symbole
% pour leur expansion.
@
echo
off
set
/a "VAR1=
1"
set
/a "VAR2=
VAR1"
echo
VAR1: %VAR1%
echo
VAR2: %VAR2%
pause
Si un nom de variable inexistante ou indéfinie est utilisé, alors elle prend la valeur 0.
1.
2.
3.
4.
5.
@
echo
off
set
/a "VAR2=
VAR1"
echo
VAR2: %VAR2%
pause
Les sous-expressions sont constituées de nombres,
d'opérateurs et éventuellement de parenthèses, ces dernières s'utilisent
de la même manière qu'en mathématiques et elles n'ont pas besoin d'être
échappées si, et seulement si, les guillemets sont utilisés. Toutes les
constituantes d'une sous-expression sont soumises à la préséance,
c'est-à-dire l'ordre dans lequel les diverses constituantes vont être
évaluées. L'ordre de préséance utilisé est le suivant (dans l'ordre
décroissant d'évaluation) :
- le groupement soit ( ) ;
- les opérateurs unaires ! ~ - ;
- les opérateurs arithmétiques * / % ;
- les opérateurs arithmétiques + - ;
- le décalage logique << >> ;
- le ET au niveau du bit & ;
- le OU exclusif au niveau du bit ^ ;
- le OU inclusif au niveau du bit | ;
- l''attribution = *= /= %= += -= &= ^= |= <<= >>= ;
- le séparateur d'expression ,.
Il faut également noter que l'utilisation du nombre
-2 147 483 648 tel quel dans une expression provoque une erreur, c'est
dû au transtypage effectué par l'interpréteur. Ce dernier évalue d'abord
la chaîne 2 147 483 648 afin de la transtyper puis lui applique
l'opérateur unaire
-. Mais comme le
nombre 2 147 483 648 va au-delà de la définition d'un nombre sur 32bits,
l'opération génère une erreur, exemple avec le script suivant.
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
Une autre méthode qui fonctionne pour ce cas est la suivante.
1.
2.
3.
4.
5.
@
echo
off
set
"SInt32=
-2147483648"
set
/a "SInt32"
echo
%SInt32%
pause
II-D-1. Les opérations arithmétiques▲
La commande
set /a prend en charge les cinq opérations arithmétiques suivantes :
- l'addition ;
- la soustraction ;
- la multiplication ;
- la division ;
- et le modulo (le modulo est une opération qui sert à récupérer le reste d'une division).
Opérateur arithmétique |
Opérateur d'attribution |
Opération effectuée |
+ |
+= |
Addition |
- |
-= |
Soustraction |
* |
*= |
Multiplication |
/ |
/= |
Division |
% |
%= |
Modulo |
Tableau 4 : Opérations arithmétiques
Une note particulière pour le modulo : dans un script de commande, le symbole du modulo
% doit être préfixé du caractère d'échappement
%. Dans ce cas, c'est un piège dans lequel le novice peut se perdre, le premier symbole
% est un caractère d'échappement qui permet au deuxième symbole
%, le modulo, d'être pris en compte à l'exécution du script. Dans l'interpréteur de commande, le modulo (
%) n'as pas besoin d'être échappé, c'est une spécificité du script. Ainsi la commande suivante fonctionne dans l'interpréteur.
set
/a "Mod=
5 % 2"
Alors que celle-ci ne fonctionne pas.
set
/a "Mod=
5 %% 2"
Dans un script, par contre, c'est l'inverse.
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
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.
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"
set
/a "Huit=
8+Dix","Neuf=
10-Un"
echo
%Un%
, %Deux%
, %Trois%
, %Quatre%
, %Cinq%
, %Six%
, %Sept%
, %Huit%
, %Neuf%
echo.
pause
L'attribution permet d'effectuer des opérations sur des
variables existantes, si une variable inexistante ou indéfinie est
utilisée, elle prend la valeur 0.
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"
set
/a "Attrib0+=
1"
set
/a "Attrib1+=
1"
set
/a "Attrib2*
=
2"
set
/a "Attrib3-=
2"
set
/a "Attrib4/=
2"
set
/a "Attrib5%%=
2"
echo
%Attrib0%
, %Attrib1%
, %Attrib2%
, %Attrib3%
, %Attrib4%
, %Attrib5%
echo.
pause
II-D-2. Les opérations logiques▲
Elles ne peuvent s'effectuer que sur des nombres et suivent
les règles de l'algèbre de Boole. Les opérations logiques prises en
charge par la commande
set /a possèdent, elles aussi, des opérateurs d'attribution. Exemple avec le script 23.
Opérateur logique |
Opérateur d'attribution |
Description |
& |
&= |
ET logique au niveau du bit. (Bitwise AND) |
^ |
^= |
OU exclusif au niveau du bit. (Bitwise XOR) |
| |
|= |
OU inclusif au niveau du bit. (Bitwise OR) |
<< |
<<= |
Décalage logique vers la gauche. Le bit entrant est à zéro (pour l'élévation par puissances de deux). |
>> |
>>= |
Décalage logique vers la droite. Le bit entrant est égal au bit de signe (pour la propagation du bit de signe). |
Tableau 5 : Les opérateurs logiques
@
echo
off
set
/a "Val1=
-2147483647 - 1"
set
/a "Val2=
1"
set
/a "Val1SwitchL01=
Val1<
<
1","Val1SwitchL15=
Val1<
<
15","Val1SwitchL31=
Val1<
<
31"
set
/a "Val2SwitchL01=
Val2<
<
1","Val2SwitchL15=
Val2<
<
15","Val2SwitchL31=
Val2<
<
31"
set
/a "Val1SwitchR01=
Val1>
>
1","Val1SwitchR15=
Val1>
>
15","Val1SwitchR31=
Val1>
>
31"
set
/a "Val2SwitchR01=
Val2>
>
1","Val2SwitchR15=
Val2>
>
15","Val2SwitchR31=
Val2>
>
31"
set
/a "Val1AttribSwitchR=
Val1","Val1AttribSwitchR>
>
=
15"
set
/a "Val2AttribSwitchL=
Val2","Val2AttribSwitchL<
<
=
15"
echo
%Val1%
^<
^<
1: %Val1SwitchL01%
echo
%Val1%
^<
^<
15: %Val1SwitchL15%
echo
%Val1%
^<
^<
31: %Val1SwitchL31%
echo.
echo
%Val2%
^<
^<
1: %Val2SwitchL01%
echo
%Val2%
^<
^<
15: %Val2SwitchL15%
echo
%Val2%
^<
^<
31: %Val2SwitchL31%
echo.
echo
%Val1%
^>
^>
1: %Val1SwitchR01%
echo
%Val1%
^>
^>
15: %Val1SwitchR15%
echo
%Val1%
^>
^>
31: %Val1SwitchR31%
echo.
echo
%Val2%
^>
^>
1: %Val2SwitchR01%
echo
%Val2%
^>
^>
15: %Val2SwitchR15%
echo
%Val2%
^>
^>
31: %Val2SwitchR31%
echo.
echo
%Val1%
^>
^>
=
15: %Val1AttribSwitchR%
echo
%Val2%
^<
^<
=
15: %Val2AttribSwitchL%
echo.
set
/a "Val3=
1431655765"
set
/a "Val4=
-858993460"
set
/a "Val3LogicalOrVal4=
Val3 |
Val4"
set
/a "Val3LogicalXorVal4=
Val3 ^ Val4"
set
/a "Val3LogicalAndVal4=
Val3 &
Val4"
set
/a "Val3AttribOrVal4=
Val3","Val3AttribOrVal4|
=
Val4"
set
/a "Val3AttribXorVal4=
Val3","Val3AttribXorVal4^=
Val4"
set
/a "Val3AttribAndVal4=
Val3","Val3AttribAndVal4&
=
Val4"
echo
%Val3%
^|
%Val4%
:
%Val3LogicalOrVal4%
echo
%Val3%
^|
=
%Val4%
:
%Val3AttribOrVal4%
echo.
echo
%Val3%
^^ %Val4%
:
%Val3LogicalXorVal4%
echo
%Val3%
^^=
%Val4%
:
%Val3AttribXorVal4%
echo.
echo
%Val3%
^&
%Val4%
:
%Val3LogicalAndVal4%
echo
%Val3%
^&
=
%Val4%
:
%Val3AttribAndVal4%
echo.
pause
II-D-3. Les opérateurs unaires▲
N.B. : Dans les sections II.D.3,
II.E et
II.F 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.
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
set
/a "Val1=
2147483647"
set
/a "Val2=
1"
set
/a "Val3=
0"
set
/a "LogicalNOT_Val1=
!Val1"
set
/a "LogicalNOT_Val2=
!Val2"
set
/a "LogicalNOT_Val3=
!Val3"
set
/a "BitwiseNOT_Val1=
~Val1"
set
/a "BitwiseNOT_Val2=
~Val2"
set
/a "BitwiseNOT_Val3=
~Val3"
set
/a "NEG_Val1=
-Val1"
set
/a "NEG_Val2=
-Val2"
set
/a "NEG_Val3=
-Val3"
echo
╔════════════════════════════════════════╗
echo
║ Nombre ║
echo
╔════════════╬═════════════╦═════════════╦════════════╣
echo
║ Opérateur ║ %Val1%
║ %Val2%
║ %Val3%
║
echo
╠════════════╬═════════════╬═════════════╬════════════╣
echo
║ LogicalNOT ║ %LogicalNOT_Val1%
║ %LogicalNOT_Val2%
║ %LogicalNOT_Val3%
║
echo
╠════════════╬═════════════╬═════════════╬════════════╣
echo
║ BitwiseNOT ║ %BitwiseNOT_Val1%
║ %BitwiseNOT_Val2%
║ %BitwiseNOT_Val3%
║
echo
╠════════════╬═════════════╬═════════════╬════════════╣
echo
║ NEG ║ %NEG_Val1%
║ %NEG_Val2%
║ %NEG_Val3%
║
echo
╚════════════╩═════════════╩═════════════╩════════════╝
echo.
pause
II-E. Les nombres entiers signés en notation hexadécimale▲
Les nombres en notation hexadécimale doivent être déclarés
comme des nombres entiers en notation décimale (ce que nous avons
utilisé jusqu'à maintenant) avec la commande
set /a "<expression>" et toujours être préfixés par
0x (chiffre zéro suivi de la lettre « x »). La commande
set /a
a pour effet de transformer toutes les valeurs entrées en valeurs
numériques. Si c'est une chaîne de caractères alors la commande
cherchera une variable portant ce nom, si c'est un nombre hexadécimal
alors la commande
set /a codera le
nombre tel quel en binaire. Cependant, même si un nombre est fourni en
notation hexadécimale, l'interpréteur l'expanse toujours en notation
décimale, exemple avec le script 25.
1.
2.
3.
4.
5.
@
echo
off
set
/a "Hexa=
0x1 + 0x3"
echo
Résultat: %Hexa%
pause
Le résultat du script 25 nous montre que le calcul s'effectue correctement, de même avec le script 26.
1.
2.
3.
4.
5.
6.
@
echo
off
set
/a "Hexa=
0x5 + 0x8"
echo
Résultat: %Hexa%
pause
Jusque-là tout va bien même si le résultat n'est pas en
notation hexadécimale, il est quand même celui attendu. Le problème
c'est que l'interpréteur de commande utilise toujours la représentation
en complément à deux pour coder un nombre entier signé. Ainsi, dès lors
que l'on utilise un nombre en notation hexadécimale supérieur à
0x7FFFFFFF (soit 2 147 483 647 en notation décimale), ce nombre est en
fait un nombre négatif comme le montre le script 27.
1.
2.
3.
4.
@
echo
off
set
/a "Hexa=
0x80000000"
echo
Résultat: %Hexa%
pause
Un résultat plutôt déroutant pour quiconque ne s'y attend
pas, en effet la représentation en complément à deux code les nombres
négatifs de 0xFFFFFFFF, soit -1, à 0x80000000, soit -2 147 483 648.
Exemple avec le script 28.
1.
2.
3.
4.
5.
6.
@
echo
off
set
/a "Hexa=
0x80000000 + 0x7FFFFFFF"
echo
Résultat: %Hexa%
pause
Comme vous pouvez le voir, on additionne 0x80000000 (soit
-2 147 483 648 en représentation hexadécimale en complément à deux) à
0x7FFFFFFF (soit +2 147 483 647, toujours dans la même représentation)
ce qui nous donne le résultat de -1, le résultat est un nombre entier
signé décimal utilisant la représentation en complément à deux codé sur
32bits soit 0xFFFFFFFF. Pour récupérer un nombre hexadécimal, il faut le
calculer à l'aide d'un algorithme de divisions successives ; et pour
les nombres négatifs, de l'opérateur unaire « Bitwise NOT » dont dispose
la commande
set /a, voir le script 29.
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
set
"SInt32=
-2147483648"
set
/a "SInt32"
set
/a "SInt32=
~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
/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"
set
"LSW=
%Nibble3%
%Nibble2%
%Nibble1%
%Nibble0%
"
set
"MSW=
%Nibble7%
%Nibble6%
%Nibble5%
%Nibble4%
"
echo
%MSW%
%LSW%
set
"SInt32=
2147483647"
set
/a "SInt32"
set
/a "Nibble0=
SInt32 %% 16","HighOrder0=
SInt32 / 16"
set
/a "Nibble1=
HighOrder0 %% 16","HighOrder1=
HighOrder0 / 16"
set
/a "Nibble2=
HighOrder1 %% 16","HighOrder2=
HighOrder1 / 16"
set
/a "Nibble3=
HighOrder2 %% 16","HighOrder3=
HighOrder2 / 16"
set
/a "Nibble4=
HighOrder3 %% 16","HighOrder4=
HighOrder3 / 16"
set
/a "Nibble5=
HighOrder4 %% 16","HighOrder5=
HighOrder4 / 16"
set
/a "Nibble6=
HighOrder5 %% 16"
set
/a "Nibble7=
HighOrder5 / 16"
set
"LSW=
%Nibble3%
%Nibble2%
%Nibble1%
%Nibble0%
"
set
"MSW=
%Nibble7%
%Nibble6%
%Nibble5%
%Nibble4%
"
echo
%MSW%
%LSW%
pause
La sortie du script 29 nous donne bien des nombres
hexadécimaux, mais dans une représentation décimale (8 0 0 0 0 0 0 0
soit -2 147 483 648 codé 0x80000000 et 7 15 15 15 15 15 15 15 soit
2 147 483 647 codé 0x7FFFFFFF), pour pouvoir les convertir dans une
notation hexadécimale ; il nous faut utiliser les conditions que nous
étudierons au prochain chapitre.
II-F. Les nombres entiers signés en notation octale▲
Les nombres en notation octale fonctionnent comme les autres
et supportent les mêmes opérations. Pour être considérés par
l'interpréteur comme des nombres en notation octale, ils doivent être
préfixés par
0 (chiffre zéro). Ils vont
donc de -2 147 483 648 à 2 147 483 647 (codés sur 32bits) soit en
notation octale -020000000000 à +017777777777. Les nombres en notation
octale sont codés tels quels en binaire, mais le principe selon lequel
tout nombre est expansé en entier signé en notation décimale codé en
complément à deux s'applique aussi pour eux. Notez aussi qu'ils ont le
même problème de transtypage que les nombres décimaux, exemple avec le
script 30.
@
echo
off
set
/a "Octal1=
-017777777777 - 1"
set
/a "Octal2=
017777777777"
set
/a "Octal3=
Octal1 + Octal2"
echo
Octal1: %Octal1%
echo
Octal2: %Octal2%
echo
Octal3: %Octal3%
pause
Notez aussi que l'utilisation des nombres « 08 » et « 09 »
génère toujours une erreur du fait qu'ils sont préfixés par « 0 » ; et,
que « 8 » et « 9 » ne sont pas des chiffres octaux. Lorsque la commande
set /a
rencontre un « 0 » en début de nombre, elle considère que tous les
chiffres qui se trouvent après sont des octaux et lorsqu'elle rencontre
« 8 » ou « 9 », le transtypage ne peut s'effectuer et la commande se
termine sur une erreur. Exemple avec le script 31.
@
echo
off
echo
Nombre 08:
set
/a "Octal1=
08"
echo.
echo
Nombre 09:
set
/a "Octal1=
09"
echo.
pause
Les conditions sont supportées par la commande
if.
Elle permet d'effectuer des comparaisons de toutes sortes et d'exécuter
des commandes en fonction du résultat de cette comparaison.
else et
else if sont, quant à eux, des paramètres de la commande
if et ne peuvent être utilisés qu'à l'intérieur de la commande
if.
III-A. If, else, else if, not et /i▲
La commande
if se traduit simplement par « si » : « si » la condition est vraie, fais ceci ; elle peut être accompagnée par le paramètre
else qui se traduit par « sinon » : « si » la condition est vraie, fais ceci, « sinon » fais cela. La syntaxe de la commande
if est la suivante.
if
<
con
dition>
<
commande1>
[else
<
commande2>
]
Comme vous pouvez le voir sur la syntaxe de la commande
if,
if vient en premier suivi de sa
<condition> et d'une commande (
<commande1>), puis vient
else et une commande (
<commande2>), on peut le traduire par :
- si (if) la <condition> se vérifie alors la <commande1> s'exécute ;
- sinon (else) c'est la <commande2> qui s'exécute.
Le paramètre
else ne peut être utilisé seul, il doit toujours être utilisé avec la commande
if, et être sur la même ligne. Il y a deux règles importantes à garder en tête lorsqu'on utilise des conditions :
- la première est que l'on ne peut utiliser qu'un maximum de
2048 caractères (sous Windows XP et inférieur) ou un peu plus de 4096
caractères (sous Windows Vista et supérieur) par ligne de commande,
limite très vite atteinte avec plusieurs conditions, plus leurs
commandes et leurs paramètres ;
- et la deuxième est que les parenthèses sont prises en
compte par l'interpréteur de commande comme des opérateurs de bloc,
c'est pour cela qu'il faut les échapper lorsque l'on ne s'en sert pas à
cette fin.
Les opérateurs de bloc permettent d'utiliser un bloc de
commande en lieu et place d'une commande. Ainsi, chaque fois que la
syntaxe autorise l'utilisation d'une commande, celle-ci peut être
remplacée par un bloc de commandes.
Ainsi la syntaxe de la commande
if peut être modifiée de la manière suivante.
Syntaxe des conditions if et else avec parenthèses
Sélectionnez
if
<
con
dition>
(
<
commande1>
<
commande2>
) else
(
<
commande3>
<
commande4>
)
L'utilisation des parenthèses comme opérateurs de bloc permet
d'exécuter plusieurs commandes, par condition vérifiée, au lieu d'une
seule. La parenthèse ouvrante associée à une condition doit se trouver
sur la même ligne que cette dernière ; si une autre condition est
utilisée conjointement, alors elle doit se trouver sur la même ligne que
la parenthèse fermante associée à la condition qui la précède.
La commande
if autorise aussi le paramètre
else if qui se traduit par « sinon si », ce dernier permet de poser une condition supplémentaire avant l'exécution éventuelle du
else. La syntaxe serait la suivante.
if
<
con
dition1>
<
cmd1>
[else
if
<
con
dition2>
<
cmd2>
] [else
<
cmd3>
]
Les opérateurs de bloc fonctionnent de la même manière avec le paramètre
else if en plus. Il faut aussi noter que le paramètre
else if peut être utilisé autant de fois que l'on veut dans la commande
if.
Syntaxe des conditions if, else if et else
Sélectionnez
if
<
con
dition1>
(
<
commande1>
<
commande2>
) else
if
<
con
dition2>
(
<
commande3>
<
commande4>
) else
if
<
con
dition3>
(
<
commande5>
<
commande6>
) else
(
<
commande7>
<
commande8>
)
La syntaxe ci-dessus pourrait être traduite par :
- si (if) la <condition1> est vraie : exécuter <commande1> et <commande2> ;
- sinon si (else if) <condition2> est vraie : exécuter <commande3> et <commande4> ;
- sinon si (else if) <condition3> est vraie : exécuter <commande5> et <commande6> ;
- sinon (else) : exécuter <commande7> et <commande8>.
Les conditions associées à la commande
if et au paramètre
else if prennent le modificateur
not qui permet d'exécuter une commande si la condition est fausse. Le modificateur
not ne s'applique pas à toutes les conditions de la même structure de contrôle. Si le paramètre
else if est utilisé conjointement, son comportement dépendra de la présence, ou non, du modificateur
not à l'intérieur de la condition.
if
not
<
con
dition1>
<
cmd1>
else
if
<
con
dition2>
<
cmd2>
else
<
cmd3>
La commande ci-dessus se traduit par :
- si (if) la <condition1> est fausse : exécuter la <cmd1> ;
- sinon (else if), si la <condition2> est vraie : exécuter la <cmd2> ;
- sinon (else), si la <condition1> est vraie et que la <condition2> est fausse, exécuter la <cmd3>.
Les conditions associées à la commande
if et au paramètre
else if prennent le modificateur
/i
qui permet de ne pas tenir compte de la casse lorsque la condition
traite des chaînes de caractères. Ce modificateur ne fonctionne que sur
les conditions qui traitent des chaînes de caractères. Si le paramètre
else if est utilisé conjointement, son comportement dépendra de la présence, ou non, du modificateur
/i à l'intérieur de la condition.
if
/i <
con
dition1>
<
cmd1>
else
if
/i <
con
dition2>
<
cmd2>
else
<
cmd3>
Se traduit par :
- si (if) la <condition1> est vraie, sans tenir compte de la casse, exécuter la <cmd1> ;
- sinon si (else if) la <condition2> est vraie, sans tenir compte de la casse, exécuter la <cmd2> ;
- sinon (else), si la <condition1> est fausse sans tenir compte de la casse et que la <condition2> est fausse sans tenir compte de la casse, exécuter la <cmd3>.
Nous allons maintenant voir quelles sont les conditions supportées par la commande
if et le paramètre
else if.
III-B. La condition « exist <path> »▲
La condition
exist <path> permet de tester si un chemin d'accès ou un fichier existe. Elle peut être utilisée avec le paramètre
else if et le modificateur
not. Le modificateur
/i ne fonctionne pas sur cette condition. Sa syntaxe est la suivante, où
<path> est le chemin d'accès ou le fichier à tester. Si le chemin d'accès contient des espaces, il doit être placé entre guillemets.
if
[not
] exist
["]<
path
>
["] <
commande>
Exemple avec le script 32.
1.
2.
3.
@
echo
off
if
exist
"%cd%
" echo
%cd%
existe bien.
pause
III-C. La condition « defined <variable> »▲
La condition
defined <variable> permet de tester si une variable a une valeur définie. Elle peut être utilisée avec le paramètre
else if, et le modificateur
not. Le modificateur
/i ne fonctionne pas sur cette condition. Voir le script 33.
if
[not
] defined
<
variable>
<
commande>
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
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>
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
@
echo
off
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
endlocal
III-E. La condition « cmdextversion <opérande> »▲
La condition
cmdextversion <opérande> permet de tester si le numéro de version des extensions de commande est égal ou supérieur au nombre donné en
<opérande>.
Le numéro de version des extensions de commande est à prendre en
compte, car selon la version des extensions, le traitement des commandes
peut être modifié. Reportez-vous à l'aide concernant les commandes
citées dans le tableau 7 pour plus de détails. Chaque commande modifiée
l'est d'une manière qui lui est propre ; ainsi la commande
assoc ne sera pas modifiée de la même manière que la commande
start, tout simplement parce qu'elle n'exécute pas du tout la même opération.
DEL ou ERASE |
COLOR |
CD ou CHDIR |
MD ou MKDIR |
PROMPT |
PUSHD |
POPD |
SET |
SETLOCAL |
ENDLOCAL |
IF |
FOR |
CALL |
SHIFT |
GOTO |
START |
ASSOC |
FTYPE |
Tableau 7 : Commandes soumises aux extensions
La condition
cmdextversion <opérande>
est toujours fausse si les extensions de commande sont désactivées ; la
première version des extensions est la version « 1 » et le numéro de
version est incrémenté de 1 à chaque nouvelle version. Si le numéro de
version des extensions est égal ou supérieur à la valeur donnée dans la
condition
cmdextversion <opérande> alors la condition est vraie ; les extensions de commande étant rétrocompatibles d'une version à l'autre. Si le modificateur
not est utilisé, la condition est vraie si le numéro de version des extensions de commande est inférieur au nombre donné en
<opérande>. La condition
cmdextversion <opérande> ne prend pas le modificateur
/i. Le tableau 8 donne la liste des révisions d'extensions de commande en fonction du système d'exploitation.
Numéro de version |
Compatibilité |
1 |
Windows 2000. |
2 |
Tous les systèmes NT XP et supérieur. |
Tableau 8 : Révisions des extensions de commande
if
[not
] cmdextversion
<
opérande>
<
commande>
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
@
echo
off
setlocal
enableextensions
if
not
cmdextversion
1 (
echo
CmdExtVersion
1: Not
Detected.
) else
(
echo
CmdExtVersion
1: Detected.
)
if
cmdextversion
2 (
echo
CmdExtVersion
2: Detected.
) else
(
echo
CmdExtVersion
2: Not
Detected.
)
if
cmdextversion
3 (
echo
CmdExtVersion
3: Detected.
) else
(
echo
CmdExtVersion
3: Not
Detected.
)
pause
III-F. La condition « <chaîne1> <comparateur> <chaîne2> »▲
La condition
<chaîne1> <comparateur> <chaîne2>
permet d'effectuer des comparaisons sur des chaînes et des nombres. La
comparaison de chaînes est effectuée au moyen d'un XOR au niveau du bit
entre chaque octet d'une chaîne puis les résultats obtenus sur les
différentes chaînes sont comparés numériquement. Si les deux chaînes ne
sont constituées que de chiffres alors elles sont transtypées en nombre
puis évaluées numériquement. Les comparateurs, pris en compte par la
condition
<chaîne1> <comparateur> <chaîne2>, sont listés dans le tableau 9. La condition
<chaîne1> <comparateur> <chaîne2> prend les modificateurs
not et
/i. Sa syntaxe est la suivante, si les chaînes contiennent des espaces, elles doivent être placées entre guillemets.
if
[/i] [not
] ["]<
chaîne1>
["] <
comparateur>
["]<
chaîne2>
["] <
cmd
>
Comparateur |
Description |
EQU |
« EQUal », la condition est vraie si les deux chaînes sont égales. |
NEQ |
« Not EQual », la condition est vraie si les deux chaînes sont différentes. |
LSS |
« LeSS », la condition est vraie si chaîne1 est inférieure à chaîne2. |
LEQ |
« Less or EQual », la condition est vraie si chaîne1 est inférieure ou égale à chaîne2. |
GTR |
« GreaTeR », la condition est vraie si chaîne1 est supérieure à chaîne2. |
GEQ |
« Greater or EQual », la condition est vraie si chaîne1 est supérieure ou égale à chaîne2. |
Tableau 9 : Opérateurs de comparaison
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"
set
/a "C1X=
0x55^0x6E^0x20^0x50^0x65^0x74^0x69^0x74^0x20^0x74^0x65^0x73^0x74"
set
"Chaîne2=
Un petit test"
set
/a "C2X=
0x55^0x6E^0x20^0x70^0x65^0x74^0x69^0x74^0x20^0x74^0x65^0x73^0x74"
set
"Chaîne3=
Un grand test"
set
/a "C3X=
0x55^0x6E^0x20^0x67^0x72^0x62^0x6E^0x64^0x20^0x74^0x65^0x73^0x74"
set
"Chaîne4=
41"
set
"Chaîne5=
12"
echo
Chaîne 1 Chaîne 2 Chaîne 3
echo
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
echo
Chaîne %Chaîne1%
%Chaîne2%
%Chaîne3%
echo
Évaluation %C1X%
%C2X%
%C3X%
echo.
if
"%Chaîne1%
" GTR
"%Chaîne2%
" (
echo
"%Chaîne1%
" est supérieur à "%Chaîne2%
".
) else
if
"%Chaîne1%
" EQU
"%Chaîne2%
" (
echo
"%Chaîne1%
" est égal à "%Chaîne2%
".
) else
if
"%Chaîne1%
" LSS
"%Chaîne2%
" (
echo
"%Chaîne1%
" est inférieur à "%Chaîne2%
".
)
if
/i "%Chaîne1%
" GTR
"%Chaîne2%
" (
echo
"%Chaîne1%
" est supérieur à "%Chaîne2%
".
) else
if
/i "%Chaîne1%
" EQU
"%Chaîne2%
" (
echo
"%Chaîne1%
" est égal à "%Chaîne2%
".
) else
if
/i "%Chaîne1%
" LSS
"%Chaîne2%
" (
echo
"%Chaîne1%
" est inférieur à "%Chaîne2%
".
)
if
"%Chaîne1%
" GTR
"%Chaîne3%
" (
echo
"%Chaîne1%
" est supérieur à "%Chaîne3%
".
) else
if
"%Chaîne1%
" EQU
"%Chaîne3%
" (
echo
"%Chaîne1%
" est égal à "%Chaîne3%
".
) else
if
"%Chaîne1%
" LSS
"%Chaîne3%
" (
echo
"%Chaîne1%
" est inférieur à "%Chaîne3%
".
)
if
/i "%Chaîne1%
" GTR
"%Chaîne3%
" (
echo
"%Chaîne1%
" est supérieur à "%Chaîne3%
".
) else
if
/i "%Chaîne1%
" EQU
"%Chaîne3%
" (
echo
"%Chaîne1%
" est égal à "%Chaîne3%
".
) else
if
/i "%Chaîne1%
" LSS
"%Chaîne3%
" (
echo
"%Chaîne1%
" est inférieur à "%Chaîne3%
".
)
if
"%Chaîne3%
" GTR
"%Chaîne2%
" (
echo
"%Chaîne3%
" est supérieur à "%Chaîne2%
".
) else
if
"%Chaîne3%
" EQU
"%Chaîne2%
" (
echo
"%Chaîne3%
" est égal à "%Chaîne2%
".
) else
if
"%Chaîne3%
" LSS
"%Chaîne2%
" (
echo
"%Chaîne3%
" est inférieur à "%Chaîne2%
".
)
if
/i "%Chaîne3%
" GTR
"%Chaîne2%
" (
echo
"%Chaîne3%
" est supérieur à "%Chaîne2%
".
) else
if
/i "%Chaîne3%
" EQU
"%Chaîne2%
" (
echo
"%Chaîne3%
" est égal à "%Chaîne2%
".
) else
if
/i "%Chaîne3%
" LSS
"%Chaîne2%
" (
echo
"%Chaîne3%
" est inférieur à "%Chaîne2%
".
)
echo.
echo.
echo
Chaîne 4 Chaîne 5
echo
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
echo
Chaîne %Chaîne4%
%Chaîne5%
echo.
if
%Chaîne4%
GTR
%Chaîne5%
(
echo
%Chaîne4%
est supérieur à %Chaîne5%
.
) else
if
%Chaîne4%
EQU
%Chaîne5%
(
echo
%Chaîne4%
est égal à %Chaîne5%
.
) else
if
%Chaîne4%
LSS
%Chaîne5%
(
echo
%Chaîne4%
est inférieur à %Chaîne5%
.
)
echo.
pause
III-G. La condition « "<chaîne1>"=="<chaîne2>" »▲
La condition
"<chaîne1>"=="<chaîne2>"
permet de tester une égalité entre des chaînes de caractères. Les
chaînes doivent être placées entre guillemets si elles sont susceptibles
de contenir des espaces. Cette condition prend les modificateurs
not et
/i.
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
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.
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
setlocal
set
/p "SInt32=
Entrez un nombre entre -2147483648 et 2147483647: "
set
/a "SInt32"
set
"NegNum=
false"
if
%SInt32%
LSS
0 (
set
/a "SInt32=
~SInt32"
set
"NegNum=
true"
)
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"
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"
)
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"
)
set
"LSW=
%Nibble3%
%Nibble2%
%Nibble1%
%Nibble0%
"
set
"MSW=
%Nibble7%
%Nibble6%
%Nibble5%
%Nibble4%
"
echo
0x%MSW%
%LSW%
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.
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
>
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
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 VI.
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.
1.
2.
3.
4.
5.
@
echo
off
:
LabelUn
call
:
LabelDeux
:
LabelDeux
goto
:
LabelUn
IV-D-2. Sortie de fonction▲
La commande
goto prend aussi le label
:eof
(End Of File), qui prend la valeur de l'adresse de retour du contexte
en cours, soit la ligne se trouvant immédiatement après le dernier appel
effectué.
La commande
goto n'ajoute rien dans la pile, ainsi l'exécution d'un
goto ne peut occasionner de fuite de mémoire. Dans le cas précis de l'exécution d'un
goto :eof, la commande
goto
incrémente le pointeur de pile de la taille totale des adresses des
arguments d'appel et de la taille de l'adresse de retour, permettant à
la pile de revenir à l'état d'avant l'appel, puis effectue un saut
inconditionnel à l'adresse de retour, soit la ligne suivant le dernier
appel. Ainsi, si l'on modifie le script 40 de la manière suivante
(script 41), on obtient un script qui ne finit jamais, car il n'y a ni
fuite de mémoire (chaque appel de la fonction
:LabelDeux finit par
goto :eof), ni point de sortie (il y aura toujours une commande à exécuter, l'exécution revient toujours au
:LabelUn). Seul l'appui sur les touches
Ctrl+C permet de quitter le script.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
@
echo
off
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 :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
@
echo
off
call
:
MonLabel
Param1 Param2
pause
goto
:
eof
:
MonLabel
echo
%*
echo
%0
echo
%1
echo
%2
goto
:
eof
L'utilisation des paramètres suit plusieurs règles :
- les paramètres ne sont pas limités en nombre dans la
commande d'appel, mais la règle des 2048/4096 caractères maximum par
ligne de commande s'applique ;
- par souci de compatibilité avec les systèmes Windows XP et
antérieurs, il est préférable de ne pas utiliser des paramètres avec un
index supérieur à 9, qui restent cependant accessibles via la commande shift (voir section IV.E.2).
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 :
1.
2.
3.
4.
5.
6.
7.
8.
9.
@
echo
off
cls
call
:
MonLabel
%cmdcmdline%
pause
goto
:
eof
:
MonLabel
echo
%~dpnx$ComSpec:1
goto
:
eof
IV-E-2. La commande shift▲
La commande
shift permet de
décaler l'index de tous les paramètres de -1, afin d'accéder aux index
supérieurs à neuf, ou de créer un mécanisme de gestion des paramètres
(voir la
mise en application de ce chapitre). 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 :.
1.
2.
3.
4.
5.
6.
7.
8.
9.
@
echo
off
cls
call
:
MonLabel
"rien" %cmdcmdline%
pause
exit
/b
:
MonLabel
shift
/1
echo
%~dpnx$ComSpec:1
goto
:
eof
IV-F. Les sorties▲
La sortie d'un contexte ou d'un processus peut s'effectuer via les commandes
exit et
goto :eof. Dans le cas de
goto :eof, si tous les contextes ont correctement été dépilés, alors la dernière adresse dépilée est :
- soit l'adresse de sortie du processus dans le cas d'un script appelé en cliquant dessus ;
- soit l'adresse de retour à l'interpréteur si le script a été lancé depuis celui-ci.
La commande
exit, elle, permet de sortir du processus hôte, quel que soit le nombre de contextes empilés. Si la commande
exit est utilisée avec le paramètre
/b,
alors la sortie s'effectuera à l'adresse de sortie du contexte et non à
l'adresse de sortie du processus hôte. L'intérêt de la commande
exit est qu'un code numérique de sortie peut être spécifié, contrairement à
goto :eof. La syntaxe de la commande exit est la suivante, où
<CodeSortie> est le code de sortie.
exit
[/b] [<
CodeSortie>
]
Dans le script 45, la sortie de la fonction
:LabelDeux s'effectue à l'aide d'un
goto:eof et la sortie de la fonction
:LabelQuatre s'effectue à l'aide d'
exit /b ;
dans les deux cas, le retour vers la procédure appelante s'opère
correctement, car c'est la bonne adresse de retour qui est dépilée. Dans
la fonction
:LabelSix du script 45, la commande
exit /b provoque la fin du script, car la fonction
:LabelSix a été appelée avec la commande
goto :LabelSix
qui n'a rien empilé. Comme tous les contextes précédents ont été
dépilés correctement, seule la dernière adresse de retour subsiste.
Cette adresse correspond à l'adresse de retour à l'interpréteur si le
script a été appelé depuis celui-ci ou à l'adresse de sortie du
processus, si le script a été appelé par un double-clic.
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
IV-G. Code de sortie▲
Comme expliqué précédemment, un code de sortie peut être
spécifié pour une fonction ou pour un script. Cela se fait via la
commande
exit [/b] <Code> où
<Code>
est un code de sortie numérique. Les codes de sortie sont régis par les
mêmes règles que les nombres entiers et sont codés sur 32 bits en
arithmétique signée. Ce code de sortie est fixé dans la variable
ErrorLevel afin de pouvoir être utilisé par la suite. Exemple avec le script 46 :
1.
2.
3.
4.
5.
6.
7.
8.
9.
@
echo
off
echo
%ErrorLevel%
call
:
UnLabel
echo
%ErrorLevel%
pause
exit
/b
:
UnLabel
exit
/b 1
IV-H. Script batch et adresse de retour▲
L'appel d'un script depuis l'interpréteur ou en cliquant
dessus génère toujours un contexte complet. Cependant l'appel d'un
script en ligne de commande depuis un autre script génère un contexte ne
possédant pas d'adresse de retour. Exemple avec le script 47 :
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
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.
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
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.
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 :
Seconde instance :
Si l'on modifie le script 49 en ajoutant le paramètre
/i dans la commande
start (script 50), alors le nouveau processus sera créé en héritant du contexte original du processus hôte dans lequel la variable
X n'a pas été déclarée.
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 :
Seconde instance :
Une autre application intéressante de la commande
start est l'exécution parallèle à l'aide du paramètre
/b comme dans le script 51.
N.B. : la commande
timeout n'est pas fournie en standard sur tous les systèmes Windows. Si vous ne l'avez pas, veuillez supprimer la commande
timeout du script, dé-commenter les commandes
echo,
ping et supprimer le caractère d'échappement dans la commande
ping.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
@
echo
off
if
"%1
"=
=
"foo" goto
SecondInstance
start
/b %~nx0
foo
timeout
/t 10 /nobreak
pause
exit
/b
:
SecondInstance
echo
%1
exit
IV-J. La commande setlocal▲
Cette commande permet de modifier partiellement les contextes
en créant un contexte local. Les variables et leurs valeurs, dans ce
nouveau contexte local, sont soumises aux règles inhérentes à la
création de contexte. L'avantage de cette commande est que l'on peut
créer un contexte sans passer par un appel. Ce contexte local ne possède
pas d'adresse de retour ni de paramètres d'appel. Seules les variables,
et éventuellement les extensions de commande, sont affectées. Ainsi,
toutes les variables créées ou modifiées dans ce contexte local y sont
propres. Cette modification prend fin lorsque l'exécution rencontre la
commande
endlocal. La commande
setlocal prend aussi les paramètres :
- enabledelayedexpansion qui active l'expansion retardée ;
- disabledelayedexpansion qui désactive l'expansion retardée ;
- enableextensions qui active les extensions de commande ;
- disableextensions qui désactive les extensions de commande.
setlocal
[enableextensions|
disableextensions] [enabledelayedexpansion|
disabledelayedexpansion]
IV-K. La commande endlocal▲
Cette commande termine le contexte local généré par la commande
setlocal
correspondante. Plusieurs contextes locaux pouvant être empilés les uns
au-dessus des autres, toutes les modifications apportées dans le
contexte local qui se termine sont perdues, y compris les modifications
apportées par les paramètres de la commande
setlocal. La commande
endlocal ne prend aucun paramètre. Exemple pour
setlocal et
endlocal avec le script 52 :
1.
2.
3.
4.
5.
6.
7.
8.
@
echo
off
setlocal
set
"X=
foo"
echo
%X%
endlocal
echo
%X%
pause
exit
IV-L. Mise en application des contextes▲
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.
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
if
%SInt32%
LSS
0 (
set
"NegNum=
true"
set
/a "SInt32=
~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"
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"
)
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"
)
set
"LSW=
%Nibble3%
%Nibble2%
%Nibble1%
%Nibble0%
"
set
"MSW=
%Nibble7%
%Nibble6%
%Nibble5%
%Nibble4%
"
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.
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.1 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 :
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
Si les extensions de commande sont activées, la boucle
for peut prendre un des paramètres de la liste suivante :
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.
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
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
>
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
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.
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 »
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
1>
"%cd%
\test.txt" echo
Ligne 1
1>
>
"%cd%
\test.txt" echo
Ligne 2
1>
>
"%cd%
\test.txt" echo
Ligne 3
1>
>
"%cd%
\test.txt" echo
Ligne 4
1>
>
"%cd%
\test.txt" echo
Ligne 5
1>
>
"%cd%
\test.txt" echo
;un petit commentaire
for
/f "eol=
; delims=
" %%A
in
('type
test.txt') do
echo
%%A
echo.
for
/f "eol=
; skip=
4 delims=
" %%A
in
('type
test.txt') do
echo
%%A
del
/Q test.txt
echo.
for
/f "tokens=
1-5*
" %%A
in
("1 2 3 4 5 6 7 8 9") do
(
echo
%%A
echo
%%B
echo
%%C
echo
%%D
echo
%%E
echo
%%F
)
echo.
for
/f "usebackq delims=
" %%A
in
(`type
"%cd%
\%~nx0
"`) do
(
echo
%%A
)
echo.
pause
exit
/b
Plusieurs points sont à noter dans le script 58 :
- le premier est l'utilisation de l'option eol=; qui supprime la chaîne ;un petit commentaire des résultats obtenus des deux premières boucles ;
- le second est l'utilisation de l'option skip=4 qui supprime les quatre premières lignes du résultat de la seconde boucle ;
- le troisième est l'absence de l'option delims dans la troisième boucle, en effet l'espace et la tabulation sont les délimiteurs par défaut ;
- le dernier est l'utilisation de l'option usebackq et des caractères ` (guillemet simple inversé) qui permet l'utilisation de guillemets doubles pour le chemin d'accès.
V-D. Les échappements propres à la boucle for▲
La boucle
for possède un certain nombre de caractères significatifs tel que
" ' ` ,.
Si les guillemets et autres apostrophes peuvent être gérés avec un bon
jeu d'options dans la boucle, il n'en est pas de même avec la virgule.
Ainsi, considérons le script suivant qui devrait permettre de trouver
les volumes qui sont des lecteurs de CD/DVD.
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.
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 VII) doivent aussi être échappés.
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▲
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
if
%Nibble7%
LSS
0 (
set
/a "Nibble7=
~Nibble7"
set
"NegNum=
true"
)
for
/l %%A
in
(0 1 6) do
set
/a "Nibble%%A
=
Nibble7 %% 16", "Nibble7/=
16"
if
/i "%NegNum%
"=
=
"true" for
/l %%A
in
(0 1 7) do
set
/a "Nibble%%A
=
15 - Nibble%%A
"
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).
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
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.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
@
echo
off
setlocal
enabledelayedexpansion
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
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 IV :
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.
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
Si l'on modifie le script 65 en ajoutant l'expansion retardée, l'opération s'effectue sans erreur (script 66).
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
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.
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
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.
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
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.
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
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.
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"
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
if
%Nibble7%
LSS
0 (
set
/a "Nibble7=
~Nibble7"
set
"NegNum=
true"
)
for
/l %%A
in
(0 1 6) do
set
/a "Nibble%%A
=
Nibble7 %% 16", "Nibble7/=
16"
if
/i "%NegNum%
"=
=
"true" for
/l %%A
in
(0 1 7) do
set
/a "Nibble%%A
=
15 - Nibble%%A
"
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
Depuis le début de ce document, nous avons utilisé des flux de
données sans le savoir. Dans le cas d'un script batch, un flux de
données est l'ensemble des données textuelles qu'une commande reçoit
pour son exécution et qu'elle produit pendant cette même exécution. Ce
flux de données transite via « le canal standard ».
VII-A. Le canal standard▲
Lors de l'exécution d'une commande, l'interpréteur alloue un
espace de mémoire supplémentaire pour cette commande. Cet espace de
mémoire est appelé « canal standard », il permet aux commandes exécutées
de recevoir des données et de renvoyer des messages d'erreurs ou de
réussites. Le canal standard a une entrée (aussi appelée « entrée
standard » ou « STDIN »), une sortie (aussi appelée « sortie standard »
ou « STDOUT ») et une troisième partie réservée aux messages d'erreurs
(appelés « erreurs standards » ou « STDERR »). Le flux de données arrive
dans l'entrée standard, il est traité par la commande puis un message
de réponse est envoyé dans la sortie standard. Si une erreur survient
pendant l'exécution de la commande, le message d'erreur (s'il y en a un)
est placé dans l'erreur standard. Par défaut, dès qu'une donnée est
placée dans la sortie standard ou dans l'erreur standard, elle est
affichée par l'interpréteur. Chaque partie du canal standard est
désignée par un numéro qui lui est propre, ce numéro est appelé
« handle ». Il permet à l'interpréteur d'identifier la zone de mémoire
réservée à chaque partie du canal. Notez qu'un handle désigne un fichier
chargé en mémoire, l'interpréteur traite les constituants du canal
standard comme des fichiers chargés en mémoire. Dans la suite de ce
chapitre, nous utiliserons le terme « tampon » pour désigner un fichier
chargé en mémoire afin de ne pas induire le lecteur en erreur. Le schéma
ci-dessous représente le déroulement de l'exécution d'une commande.
VII-B. Les opérateurs de redirection▲
Afin de pouvoir contrôler les « mouvements » d'un flux,
l'interpréteur fournit les opérateurs de redirection qui permettent, par
exemple, d'envoyer des données dans un fichier ou de lire les données
contenues dans un fichier. Le tableau ci-dessous donne une liste des
différents opérateurs de redirection, leur syntaxe ainsi qu'une courte
description.
Opérateur |
Syntaxe |
Description |
& |
Cmd1 & Cmd2 |
Cmd1 est exécuté puis, quel que soit son résultat, Cmd2 est exécuté. |
&& |
Cmd1 && Cmd2 |
Cmd1 est exécuté, puis si et seulement si Cmd1 ne produit pas d'erreur, Cmd2 est exécuté. |
| |
Cmd1 | Cmd2 |
Cmd1 est exécuté puis la sortie (STDOUT) de Cmd1 est envoyée dans l'entrée (STDIN) de Cmd2. |
|| |
Cmd1 || Cmd2 |
Cmd1 est exécuté puis, si et seulement si Cmd1 produit une erreur, Cmd2 est exécuté. |
< |
Cmd1 < File |
Le contenu du fichier (File) est envoyé dans l'entrée (STDIN) de Cmd1. |
> |
Cmd1 > File |
Cmd1 est exécuté, sa sortie (STDOUT) est envoyée dans
un fichier(File). L'opération s'effectue en écriture seule, le fichier
de destination n'est pas lu et cela a pour effet de le remplacer par un
fichier ne contenant que les nouvelles données. |
Handle>File |
Le contenu du tampon désigné par l'Handle est copié
dans le fichier (File) de destination. L'opération s'effectue en
écriture seule, le fichier de destination n'est pas lu et cela a pour
effet de le remplacer par un fichier ne contenant que les nouvelles
données. |
>> |
Cmd1 >> File |
Cmd1 est exécuté, sa sortie (STDOUT) est envoyée dans
un fichier (File). L'opération s'effectue en lecture et écriture, les
données contenues dans le fichier de destination sont lues puis la
sortie de Cmd1 est ajoutée à la fin. |
Handle>>File |
Le contenu du tampon désigné par l'Handle est copié
dans le fichier (File) de destination. L'opération s'effectue en lecture
et écriture, les données contenues dans le fichier de destination sont
lues puis le contenu du tampon désigné par l'Handle est ajouté à la fin.
|
<& |
<& Handle |
Redirige l'entrée standard (STDIN) dans le tampon désigné par l'Handle. |
Handle1 <& Handle2 |
Redirige le flux entrant dans le tampon désigné par l'Handle1 dans le tampon désigné par l'Handle2. |
>& |
>& Handle |
Redirige la sortie standard (STDOUT) dans le tampon désigné par l'Handle. |
Handle1>& Handle2 |
Redirige le flux sortant du tampon désigné par l'Handle1 dans le tampon désigné par l'Handle2. |
Tableau 14 : Opérateurs de redirection
VII-C. L'opérateur &▲
Cet opérateur permet d'exécuter plusieurs commandes, les unes
à la suite des autres, sans tenir compte de ce qui s'est passé durant
l'exécution de la commande précédente. Ainsi, chaque fois que
l'interpréteur rencontre l'opérateur
&, il sait qu'une autre commande est à exécuter, on parle d'exécution séquentielle.
1.
2.
3.
4.
5.
@
echo
off &
cls
echo
Affichage:
echo.
&
echo.
&
echo
1 &
echo
2
echo.
&
echo.
pause
&
exit
/b 0
VII-D. La sortie standard▲
La sortie standard (à ne pas confondre avec l'affichage
standard que l'on a vu au chapitre I) reçoit les messages d'une commande
exécutée dans l'interpréteur. Le meilleur exemple d'utilisation de la
sortie standard est la commande
echo qui
ne fait qu'envoyer les données qui lui sont passées en paramètres dans
la sortie standard. Chaque fois qu'une chaîne de caractères est envoyée
dans la sortie standard, l'interpréteur l'affiche aussitôt. Il est
possible de rediriger la sortie standard vers un fichier ou dans
l'entrée d'une autre commande. Si la sortie standard est redirigée, tout
ce qui est envoyé dans la sortie standard est ensuite placé dans sa
nouvelle destination et rien n'est affiché dans l'interpréteur. La
sortie standard peut-être désignée par son handle (
1) ou de façon implicite si le caractère
>
est utilisé dans l'opérateur de redirection. Les redirections peuvent
être placées avant ou après la commande, cela ne change rien à leurs
significations. Le schéma ci-dessous représente l'exécution d'une
commande avec redirection de la sortie standard.
VII-E. Les opérateurs > et >>▲
Ces opérateurs permettent de rediriger la sortie standard ou
le contenu d'un tampon vers un fichier. Seul le mode d'ouverture du
fichier de destination diffère entre ces deux opérateurs. Ainsi, avec
l'opérateur
>, le fichier de destination n'est pas lu, les données sont donc effacées par l'écriture des nouvelles données. Avec l'opérateur
>>,
le fichier de destination est lu puis les nouvelles données sont
ajoutées à la suite de celles déjà contenues dans le fichier de
destination. Dans le script 72, la chaîne
foo n'est pas dans le fichier
Output.txt lorsqu'il est affiché par la commande
type Output.txt, car le fichier n'a pas été lu lors de l'ajout de la chaîne
bar.
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
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.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
@
echo
off
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.
change
port /?
2>
>
HelpChangePort.txt
echo
Syntaxe de change
port:
echo.
type
HelpChangePort.txt
del
/Q HelpChangePort.txt
echo.
echo.
pause
exit
VII-G. L'entrée standard▲
L'entrée standard est tout ce que la commande reçoit comme
données, elle comprend la saisie au clavier de l'utilisateur (s'il y en a
une) ainsi que le ou les fichiers qui seront envoyés par ce biais.
L'handle de l'entrée standard est
0, mais elle peut aussi être utilisée de manière implicite si le caractère
<
est utilisé dans l'opérateur de redirection. Ci-dessous le schéma de
l'exécution d'une commande avec redirection vers l'entrée standard.
Exemple avec le script 74 :
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
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 :
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
1>
test.txt echo
Ligne 1
1>
>
test.txt echo
Ligne 2
echo
Redirection 1:
0>
>
LogInput.txt type
test.txt
echo
Output 1:
type
LogInput.txt
del
/Q LogInput.txt
echo.
echo
Redirection 2:
0<
&
3>
>
LogInput.txt type
test.txt
echo
Output 2:
type
LogInput.txt
del
/Q LogInput.txt
echo.
echo
Redirection 3:
<
&
3>
logInput.txt type
test.txt
echo
Output 3:
type
LogInput.txt
del
/Q LogInput.txt
echo.
del
/Q test.txt
pause
exit
/b 0
Comme vous pouvez le voir dans l'affichage du script 75, lorsqu'on redirige l'entrée standard directement depuis l'handle
0
vers le fichier de sortie, rien ne se produit ; en effet, il n'est pas
possible de rediriger l'entrée standard directement, il faut rediriger
son contenu dans un autre tampon pour pouvoir le rediriger vers un
fichier. Dans le deuxième et le troisième cas de notre exemple, l'entrée
standard est redirigée dans le tampon désigné par l'handle
3 avant d'être redirigée vers le fichier, la commande
type test.txt ne sert ici qu'à charger le fichier dans l'entrée standard.
VII-H. Le pseudo-périphérique NUL▲
Un pseudo-périphérique dénommé
NUL est également disponible (si on le compare avec les systèmes de type Unix, ce serait le périphérique
NUL, accessible via le point de montage
/dev/nul).
Il renvoie les données nulle part , celles-ci seront simplement
supprimées du tampon source et seront donc définitivement perdues. Ce
pseudo-périphérique trouve son utilité dans un certain nombre de cas
comme lorsqu'on ne veut pas que la sortie d'une commande apparaisse dans
l'affichage de l'interpréteur. Ce pseudo-périphérique n'a pas d'handle,
mais un pseudo-fichier de périphérique afin d'être traité par
l'interpréteur comme étant un fichier. Il n'a pas d'utilisation
implicite et il ne devrait pas non plus être utilisé en entrée puisqu'il
ne contient aucune donnée. Il peut donc être utilisé en sortie de tous
les autres tampons. Il est utilisable via le pseudo-fichier
nul, comme dans le script 76.
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
Dans le script 76, la commande
set /p "Var2=Entrez une chaîne: " 0>nul
se termine avant que l'utilisateur ne puisse entrer quoi que se soit,
cela est dû à la redirection de l'entrée standard vers le
pseudo-périphérique
NUL. Ainsi lorsque
Var2 est expansée, celle-ci est vide ; ce qui provoque l'affichage de la ligne
Commande ECHO désactivée. Si l'on retire la commande
echo. du script 76, on obtient l'affichage suivant :
La chaîne d'invite a bien été envoyée dans la sortie standard, mais comme
set /p attend des données en provenance de l'entrée standard et que celle-ci est renvoyée vers le pseudo-périphérique
NUL,
le traitement de la commande est perdu et le retour à la ligne ne se
fait jamais. Cela s'avère fort pratique lorsqu'il est nécessaire
d'ajouter du texte dans un fichier sans retour à la ligne.
Le pseudo-fichier
NUL est réservé par le système, il n'est donc pas possible de créer un fichier portant ce nom (même avec une extension de fichier).
VII-I. L'opérateur |▲
Cet opérateur, appelé « Pipe », permet de rediriger la sortie
d'une commande dans l'entrée de la commande suivante, on parle de
« pipe-line » de commande. Cependant, toutes les commandes n'ont pas la
capacité de traiter les données contenues dans l'entrée standard ; les
commandes qui peuvent faire cela sont appelées des filtres.
VII-I-1. Les filtres▲
Il existe plusieurs commandes qui agissent comme des
filtres, c'est-à-dire qu'elles ont la capacité de récupérer le contenu
de l'entrée standard, d'exécuter leurs traitements en fonction du
contenu de l'entrée standard puis d'envoyer leurs messages de résultat
dans la sortie standard afin d'être récupérés par un des différents
moyens existants tels que les boucles
for ou les redirections. Voici une liste non exhaustive des commandes pouvant être utilisées comme filtres.
Commandes |
Descriptions |
find |
Effectue une recherche de sous-chaînes dans les
chaînes de l'entrée standard (chaque chaîne de l'entrée standard est
séparée par un <CRLF>) puis renvoie les résultats dans la sortie standard. |
sort |
Effectue un tri par ordre alphabétique dans les
chaînes de l'entrée standard (chaque chaîne de l'entrée standard est
séparée par un <CRLF>) puis renvoie les résultats dans la sortie standard. |
more |
Récupère l'entrée standard puis envoie les données
par paquets de tailles équivalentes à la taille de la fenêtre de
l'interpréteur dans la sortie standard pour qu'ils soient affichés. |
findstr |
Effectue une recherche de sous-chaînes dans les
chaînes de l'entrée standard (chaque chaîne de l'entrée standard est
séparée par un <CRLF>) puis renvoie les résultats dans la sortie standard. |
Tableau 15 : Les filtres
N.B. :
<CRLF> est un retour à la ligne, formaté Windows, d'une taille de deux octets.
Exemple avec le script 77.
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
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.
1.
2.
3.
4.
5.
6.
7.
8.
@
echo
off
(echo
Con
tenu de la ligne 1 &
echo
Con
tenu de la ligne 2) 1>
>
text.txt
for
/f "usebackq delims=
" %%A
in
(`type
text.txt^|
find
/n /v ""`) do
(
for
/f "tokens=
1-2 delims=
[]" %%a
in
("%%A
") do
echo
Ligne %%a
:
%%b
)
del
/Q text.txt
pause
exit
/b 0
VII-I-3. Le filtre sort▲
La commande
sort effectue
un tri par ordre alphabétique entre les chaînes qui lui sont passées en
paramètres, ce qui permet de formater la sortie un peu comme un
dictionnaire. Elle prend plusieurs paramètres tels que
/r qui inverse l'ordre de tri,
/o qui définit un fichier de sortie pour le tri,
/t qui définit un répertoire temporaire pour l'exécution du tri,
/l qui définit les paramètres régionaux pour l'exécution du tri ou encore
/+n qui indique à partir de quel caractère commence le tri (ou
n est l'index du premier caractère en partant de 1).
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
@
echo
off
1>
text.txt echo
zxy0
1>
>
text.txt echo
abc9
1>
>
text.txt echo
9abc
1>
>
text.txt echo
0zxy
echo
Trie 1:
for
/f "delims=
" %%a
in
('type
text.txt^|
sort
/r') do
(
echo
%%a
)
echo.
&
echo
Trie 2:
for
/f "delims=
" %%a
in
('type
text.txt^|
sort
/+2') do
(
echo
%%a
)
del
/Q text.txt
echo.
pause
exit
/b 0
VII-I-4. Le filtre more▲
La commande
more s'utilise
généralement pour formater un affichage, mais elle peut aussi être
utilisée dans le but de formater un lot de données dont le traitement
nécessite une taille particulière ou de remplacer les tabulations par
des espaces.
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
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 I, 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.
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
Con
tenu du fichier :
type
txt
echo.
echo.
echo
Début de chaîne(^^bar):
type
txt|
findstr
^^bar
echo.
echo
Fin de chaîne(1$):
type
txt|
findstr
1$
echo.
echo
Début de mot(\^<
fa):
type
txt|
findstr
\^<
fa
echo.
echo
Fin de mot(2\^>
):
type
txt|
findstr
2\^>
echo.
echo
Caractère unique(\^<
f.\^>
):
type
txt|
findstr
\^<
f.\^>
echo.
echo
Zéro ou plus 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
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.
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
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 :
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
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.
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
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 :
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
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
(
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
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
(
2>
nul
set
/a "var=
09"
) &
&
(
echo
[Ok] 2 set
/a "var=
09"
) |
|
(
echo
[Erreur] 2 set
/a "var=
09"
)
(
set
/a "var=
07"
) &
&
(
echo
[Ok] 2 set
/a "var=
07"
) |
|
(
echo
[Erreur] 2 set
/a "var=
07"
)
echo
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
echo.
pause
exit
/b 0
VII-M. Les opérateurs <& et >&▲
Ces opérateurs permettent de rediriger le flux d'un tampon à l'autre. L'opérateur
<& redirige l'entrée standard ou le flux entrant dans un tampon vers un autre ; l'opérateur
>&
redirige la sortie standard ou le flux sortant d'un tampon vers un
autre. Ces opérateurs ne peuvent être utilisés qu'avec des handles,
toutes les redirections entre des tampons doivent se faire avec ces
opérateurs.
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
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 (
)
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.
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
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.
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
Con
tenu de buffer9: &
type
buffer9 &
del
/Q buffer9 &
echo.
for
/f "delims=
" %%a
in
('type
text.txt') do
(((
echo
%%a
|
findstr
\^<
foo 1>
&
3
echo
%%a
|
findstr
\^<
bar 1>
&
4
echo
%%a
|
findstr
bar\^>
1>
&
5
) 5>
>
buffer5
) 4>
>
buffer4
) 3>
>
buffer3
echo
Con
tenu de buffer3: &
type
buffer3 &
del
/Q buffer3 &
echo.
echo
Con
tenu de buffer4: &
type
buffer4 &
del
/Q buffer4 &
echo.
echo
Con
tenu de buffer5: &
type
buffer5 &
del
/Q buffer5 &
echo.
del
/Q text.txt
pause
exit
VII-O. Les pseudo-fichiers de périphérique▲
Les pseudo-fichiers de périphériques sont des alias de « nom
de fichier » qui permettent d'accéder au contenu d'un tampon de
périphérique de la même manière qu'un fichier texte. Plusieurs
périphériques sont disponibles depuis l'interpréteur, en voici la
liste :
- le canal standard ;
- les ports séries ;
- les ports parallèles ;
- l'imprimante par défaut (seulement pour les systèmes Vista et inférieurs, et certains Windows 7) ;
- le périphérique auxiliaire.
VII-O-1. Le pseudo-fichier CON▲
Pour rappel le canal standard est le tampon utilisé par les
commandes durant leurs exécutions, il est traité comme étant un
périphérique du fait que l'entrée standard est liée par défaut au
clavier et la sortie standard est liée par défaut à l'écran (la fenêtre
de l'interpréteur). Le pseudo-fichier du canal standard est
CON[:],
il a assez peu d'applications concrètes, mais en voici une qui permet
de saisir un texte sur plusieurs lignes à la différence de la commande
set /p qui ne permet les saisies que sur une seule ligne.
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.2 pour plus de détails.
Le nom de fichier
CON est
réservé par le système, il n'est donc pas possible de créer un fichier
qui porte ce nom. Les différentes constituantes du canal standard sont
accessibles via le pseudo-fichier
CON, ainsi l'entrée standard est accessible via
CON[:]IN$, la sortie standard via
CON[:]OUT$ et l'erreur standard via
CON[:]ERR$.
VII-O-2. Les pseudo-fichiers COMx▲
Les ports séries utilisent les pseudo-fichiers
COMx, où
x
est le numéro de port cible. Ce numéro peut aller de 1 à 256 même si un
PC ne peut avoir 256 ports (ces numéros de ports sont attribuables à
des ports mappés sur un réseau). L'utilisation des ports est plutôt
simple, car il suffit de les lire ou les écrire via les commandes
echo (pour l'écriture) et
type ou
more
(pour la lecture). Il faut prendre en compte qu'un port série doit être
configuré avec les mêmes paramètres que le port distant auquel il est
connecté, cela peut être fait via la commande
mode COMx ; l'affichage des paramètres se fait avec
mode COMx /status.
La configuration d'un port série ne seras pas abordée dans ce document
du fait de la quantité d'éléments à prendre en compte. Vous pouvez
effectuer des tests d'écritures/lectures sur un port série en utilisant
une « loopback » entre deux ports séries d'un même ordinateur à l'aide
d'un câble croisé 3, 5 ou 7 fils, il suffit alors de relier les deux
ports séries avec le câble croisé. Les pseudo-fichiers
COM1 à
COM9
sont réservés par le système et les autres le seront si un port distant
est mappé dessus. De même que pour le canal standard, les entrées et
les sorties sont accessibles via
COMx:IN$ et
COMx:OUT$.
Écriture sur un port série, où
<message> est la chaîne a envoyer par le port série.
echo
<
message>
>
COMx
Lecture depuis un port série.
type
COMx
more
<
COMx
VII-O-3. Les pseudo-fichiers LTPx▲
Les ports parallèles utilisent les pseudo-fichiers LTPx, où x est le numéro de port cible. Ce numéro peut aller de 1 à 256. Il existe un mappage local entre port COM et LTP via la commande mode LTPx[:]=COMy[:]. L'écriture et la lecture s'utilisent de la même façon que pour les ports séries, via les commandes echo et type ou more. Les pseudo-fichiers LTP1 à LTP9
sont réservés par le système et les autres le seront si un port distant
est mappé dessus. De même que pour le canal standard, les entrées et
les sorties sont accessibles via LTPx:IN$ et LTPx:OUT$.
VII-O-4. Le pseudo-fichier PRN▲
L'imprimante par défaut utilise le pseudo-fichier PRN
et à la différence des autres pseudo-fichiers de périphérique, il n'est
pas possible de lire les données qu'il contient. Ainsi seule l'écriture
est possible sur ce pseudo-fichier de périphérique, cela s'effectue à
l'aide d'une redirection de la sortie standard. Il faut noter que sous
Windows 7 (Ultimate et Entreprise seulement) et Windows Serveur 2008 R2,
ce pseudo-fichier de périphérique n'est plus fonctionnel. Le
pseudo-fichier PRN est réservé par le système.
VII-O-5. Le pseudo-fichier AUX▲
Le périphérique auxiliaire est mappé sur le port COM1
pour des questions de compatibilité ascendante avec les anciens
systèmes de la gamme Microsoft. Le mappage de ce port peut être modifié
via la commande change port, voir l'aide en ligne de commande pour plus d'informations. Le pseudo-fichier AUX est réservé par le système. Si le pseudo-fichier AUX est mappé sur un autre pseudo-fichier qui possède plusieurs constituants (IN$, OUT$ ou ERR$), alors ces dernières sont accessibles via AUX:IN$, AUX:OUT$ et éventuellement AUX:ERR$.
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 :
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
[
Section_One
]
[
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.
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.
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 con
duire, vous devez me laisser vos clés.
echo
Écoutez, ne le prenez pas comme ça :
c'est la loi sinon je risque un procès.
echo
Moi je vends de l'alcool, je ne vous oblige pas à le boire...
)
pause
exit
/b 0
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
[
Beer
]
foo
=
found
bar
=
lucky
foobar
=
2
[
Vodka
]
foo
=
not found
bar
=
unlucky
foobar
=
5
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.
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.
[
Copy
]
.\%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
]
.\%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 ...
La sécurité est un aspect important en informatique, les
scripts batch n'échappent pas à la règle. En effet, les possibilités
offertes par ce langage de script sont vastes, y compris pour quelqu'un
de mal intentionné. Nous allons voir les différentes méthodes
utilisables par un intrus et les solutions qu'il est possible d'y
apporter. Cependant, les solutions exposées dans ce chapitre ne sont que
des pistes de réflexion ; chaque problématique ayant des implications
différentes, il ne peut y avoir de solution générique.
IX-A. Les variables▲
Contrairement à d'autres langages, les variables sont
accessibles à tout le monde ou presque. Ainsi, n'importe quel programme
peut accéder aux variables d'environnement d'un autre programme, du
moment que celui-ci s'exécute en mode « utilisateur » et qu'il est lancé
par le même utilisateur. Seuls les programmes qui sont exécutés par
d'autres utilisateurs échappent à cette règle, à moins que le programme
qui scrute les variables d'un autre soit lancé par un administrateur ou
par le système (en tant qu'utilisateur). Pour cette partie du chapitre,
il va nous falloir le programme
ProcessExplorer de
SysInternals disponible à cette URL :
https://technet.microsoft.com/en-us/sysinternals/bb896653.aspx.
Une fois
ProcessExplorer
téléchargé, lancez-le en tant que simple utilisateur, il propose un
affichage similaire à celui ci-dessous. Les programmes surlignés en rose
ont été lancés par le système (en tant qu'utilisateur) et les
programmes surlignés en bleu ont été lancés par l'utilisateur courant.
Dans
ProcessExplorer, il est
possible d'afficher la ligne de commande ayant appelé chaque programme
(utile pour le débogage de scripts). Cela se fait via le menu
View>Select Columns…, puis dans l'onglet
Process Image cochez
Command Line puis validez en cliquant sur
Ok. Créez le script 93 et lancez-le, mais sans appuyer sur une touche au moment de la commande
pause.
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.
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.
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.
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 con
nect`) do
(
for
/f "tokens=
3" %%b
in
("%%a
") do
echo
%%b
)
pause
exit
/b 0
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).
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 :
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.
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.
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
Pour en revenir à ce qui avait été dit au
chapitre II 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 :
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 :
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 :
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
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).
Cette annexe présente des informations complémentaires sur l'utilisation des caractères.
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
.