Les batch sous Windows
Le langage batch Windows est méconnu et dénigré. Il comporte certes certains inconvénients, héritage de son
lourd passé, et qui à mon sens se concentrent sur les difficultés rencontrées dans le traitement de chaines de
caractères comportant les caractères réservés du langage (surtout %
et !
qui sont très
difficiles à échapper
).
Pour autant, il reste le premier des langages de script et remplit 99% des fonctionnalités de .JS, .VBS, ou le fameux Bash Linux, oui je sais c'est génial l'open source, c'est plein de gens heureux de faire dix fois moins bien que le payant qui n'est déjà pas brillant, mais bon là au moins on sait qu'on va avoir du n'importe quoi développé par n'importe qui LOL
Les bases
Un fichier batch est donc un script généralement interprêté par l'exécutable cmd.exe qui, s'il n'est pas lancé depuis une console windows, en ouvre une automatiquement.
Dès lors le batch a accès aux fonctionnalités de cette console, pour tout ce qui est de l'aspect graphique
texte LOL mais, plus intéressant pour l'aspect flux. Car ne nous y trompons pas, nous avons à faire
à un ETL!
Tout cet enfumage pour dire qu'à la base un batch ça sert surtout à exécuter des commandes, à lire les flux résultants et à les renvoyer vers d'autres commandes ou, à court d'idée, directement dans la console. Pour ce faire retenir la syntaxe suivante:
REM envoyer le flux dans un fichier en l'écrasant le cas échéant
commande > fichier.txt
REM envoyer le flux dans un fichier au cul des données existantes
commande >> fichier.txt
Bon c'est bien gentil mais un peu léger pour un ETL, on est obligé d'écrire dans un fichier? ... Meuh non
REM refiler le flux à une autre commande
commande | autre_commande
REM envoyer le flux dans un trou noir
commande > nul
REM envoyer le flux dans la console (simple!)
commande
Ouais ok, et dans l'autre sens? ... du trou noir il ne sort rien, pour le reste:
REM créer un flux à partir d'un fichier
type fichier.txt | commande
REM ou
commande < fichier.txt
REM créer un flux de toute pièce
echo ceci est mon flux | commande
REM tout supprimer sans confirmation
echo o | del *.*
Remarque subtile: les opérateurs de redirection de flux ne redirigent que le flux dit std. Si vous avez la chance d'utiliser des commandes bien développées, celles-ci remontent les erreurs éventuelles dans le flux d'erreur, qui lui reste le flux standard de l'appelant, en l'occurrence la console, sauf s'il est lui-même appelé par une commande au dessus qui elle redirige sa sortie dans un fichier... pertinent la remarque, non ?
Pour en finir, les commandes de base:
- cls efface la console
- echo blabla affiche
blabla
dans la console - echo off suspend l'affichage des commandes dans la console
- pause attend la frappe d'une touche, avec un message chiant à rediriger vers le trou noir
Voilà pour les bases. Ensuite, reste à voir qu'autour de ces fonctionnalités un vrai langage a été développé. Il
n'est pas case sensitive, il est constitué d'une suite de commandes, séparées par un retour charriot ou le
caractère &
et il peut gérer les variables, les boucles, les fonctions... bref que du bonheur!
Entrées / sorties
Sorties
Tout d'abord on va donner un nom à la fenêtre d'exécution du batch avec la commande title
. Ensuite
on définit la taille de la fenêtre avec la commande mode con
. Enfin on choisit la couleur du texte
et du fond avec la commande color
.
@echo off
Title Mon premier batch
mode con cols=70 lines=500
color 1E
- 0 Noir
- 1 Bleu foncé
- 2 Vert
- 3 Bleu gris
- 4 Marron
- 5 Pourpre
- 6 Kaki
- 7 Gris clair
- 8 Gris
- 9 Bleu clair
- A Vert clair
- B Cyan
- C Rouge
- D Rose
- E Jaune
- F Blanc
Pour passer à la ligne, on ne peut pas faire echo
seul, il faut écrire par exemple echo.
Pour dessiner des tableaux, on se reporte aux caractères spéciaux (cf. plus bas)
Les caractères graphiques
Il est possible de dessiner des jolis cadres en batchs en utilisant les caractères prévus à cet effet:
- É bord supérieur gauche
- Í ligne horizontale
- » bord supérieur droit
- º barre verticale
- È bord inférieur gauche
- ¼ bord inférieur droit
Cela permet de dessiner le menu principal de notre batch, ici dans une fonction qui s'appelle donc ici par
la commande call :header
(voir plus loin):
:header
cls
echo ³
echo ³ %username%
echo ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
echo %date%
echo.
echo ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
echo º Mon premier batch v 1.0 º
echo ÈÍËÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍËͼ
echo º (c) Ludovic de Jouvencel º
echo ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ
echo.
goto :EOF
Entrées
Bien sûr, le batch est interactif, il est donc prévu une commande qui permet à l'utilisateur de répondre
à une question, ou d'entrer des paramètres. C'est en fait la commande set
mais avec le qualificateur
/P
comme dans l'exemple ci-dessous.
echo.
set choix=
set /P choix=Entrez (o/n) :
if /i /%choix%/==/o/ exit
Les variables
Tout d'abord nous devons activer l'évaluation retardée car cela permet de faire de l'introspection, bref
des trucs super comme des tableaux. C'est la commande SetLocal EnableDelayedExpansion
qui doit
se trouver en début de batch.
Pour créer une variable on utilise la commande set
suivie du nom de la variable, et précédée
du qualificateur /a
lorsqu'on souhaite faire des opérations arithmétiques dessus. Ensuite on
initialise la variable, mais on peut l'initialiser à vide aussi.
Pour lire le contenu d'une variable, on l'encadre avec les symboles %
. Si l'on veut faire du
second degré, lire le contenu d'une variable dont le nom est lui même une expression du contenu d'une autre
variable, on encadre le tout du symbole !
ce qui permet de faire des tableaux associatifs, finger in
the noose:
SetLocal EnableDelayedExpansion
set rien=
set /a i=1
set TAB_1=element1
set TAB_2=element2
set TAB_3=element3
set j=1
set item=!TAB_%j%!
set /a i=%j%+1
set /a i+=1
Les manipulations de texte
Le langage permet beaucoup de choses mais attention aux caractères spéciaux qui produisent facilement des
erreurs, donc tester ces choses toujours sans echo off
pour bien comprendre ce qui se passe.
La règle est de faire en sorte que les chaines affectées aux variables soient toujours entre guillemets, cela
evite 99% des problèmes potentiels. Sinon l'interprêteur va interprêter les &
, les <
,
les >
ou encore les |
comme des commandes et c'est le plantage assuré!
Extraction de chaine
Il est très simple d'extraire des caractères contigus en spécifiant l'indice du premier caractère à extraire et le nombre de caractères à extraire. Astuce: si l'indice du caractère est négatif, on part de la fin de la chaine (-1: dernier caractère, -2: avant dernier caractère). Astuce bis: si le nombre de caractères à extraire est négatif il représente l'indice du dernier caractère en partant de la fin. Très pratique donc...
REM Premier caractere
set first=%str:~0,1%
REM Trois caracteres a partir du deuxieme
set trigramme=%str:~1,3%
REM Dernier caractere
set last=%str:~-1,1%
REM Deux derniers caracteres
set extension=%str:~-2,2%
...
Remplacement de chaine
On peut tout aussi facilement travailler sur la chaine et modifier certaines parties en remplaçant une chaine par une autre:
REM on va remplacer azr par rien
set txt=hello worazrld
echo %txt:azr=%
Les tests & contrôle de flux
Il est possible de créer des étiquettes qui commencent par le symbole :
et se poursuivent par
une chaine alphanumérique. Il est ensuite possible de s'y rendre par saut direct goto
ou par appel
de fonction call
echo.
set choix=
set /P choix=Entrez (o/n) :
if /i /%choix%/==/n/ goto :fin
echo Vous avez entre oui
exit
:fin
echo Vous avez entre non
Les tests s'écrivent en une ligne selon if condition commande
ou sur plusieurs lignes selon
if condition (
commande 1
commande 2
...
commande n)
Les opérateurs de comparaison disponibles:
- == - égal à
- EQU - égal à
- NEQ - différent de
- LSS - inférieur à
- LEQ - inférieur ou égal à
- GTR - supérieur à
- GEQ - supérieur ou égal à
/i
:getmaxdate
REM cette fonction est appelee sur chaque ligne d'un flux
set line=%~1
REM on extrait une date du flux
set linedate=%line:~6,4%%line:~3,2%%line:~0,2%
REM on cherche la date la plus récente
if /%maxd%/ LSS /%linedate%/ (set maxd=%linedate%)
goto :EOF
Il est aussi possible de déterminer si une variable est définie:
if not defined i set i=0
Les boucles
Il existe différents types de boucles. On peut parcourir des éléments spécifiés, faire varier un compteur d'une valeur initialie jusqu'à une valeur finale avec un incrément constant, ou encore parser les lignes d'un fichier ou du résultat d'une commande.
La variable de boucle possède une nomenclature particulière préfixée par %%
ce qui interdit les manipulations de chaine directement dessus. Il est par ailleurs nécessaire d'utiliser le
symbole !
pour accèder aux variables à l'intérieur d'une boucle comme on le voit dans les exemples
ci-après.
(il est par conséquent souvent nécessaire d'appeler une fonction depuis une boucle plutot que de tenter de faire le traitement sur place, pour des raisons de limitations syntaxiques, oui ce langage est loin d'être parfait LOL)
Ici on veut archiver des dossiers spécifiés, et on veut mettre leurs noms dans un tableau associatif (un vaste
choix de séparateur est géré en natif, par exemple ,
ou ;
ou simplement
:
set ARCHIVES=Contacts,Favoris,SitesWeb,Programmation
set /a i=1 & for %%a in (%ARCHIVES%) DO (set SEL!i!=1& set /a i=!i!+1)
Ici on fait donc varier un compteur de 1 à n et on affiche un texte associé:
for /L %%i in (1,1,%n%) Do (
set j= %%i
set text=!LIBELLE_%%i!
echo !j:~-2! ³ !text!
)
Ici on va lancer une commande pour lire les fichiers du répertoire et les stocker dans un tableau
FILE_xxx, simplement avec une boucle sur la commande dir /b /ad /on
.
set /a i=0 & for /f "delims=" %%p In ('dir /b /ad /on') Do (
set FILE_!i!=%%p
set /a i+=1
)
Ici on utilise les fonctionnalités de parsing pour chercher le 4ème tokens de la ligne du fichier browse.htm
qui contient la chaine form_build_id
et on appelle une fonction chargée d'extraire la valeur de la variable:
FOR /f "useback tokens=4" %%a in (`findstr "form_build_id" "%TEMP%\browse.htm"`) do call :getformid "%%a"
Remarque: le mot réservé useback
permet d'utiliser le symbole `
et
donc d'encapsuler les quotes dans la commande findstr "form_build_id" "%TEMP%\browse.htm"
. Oui,
toujours des rustines pour gérer la mauvaise gestion de l'échappement inhérente au langage...
On peut aussi spécifier un délimiteur de fin de ligne avec eol
ou encore ignorer un certain nombre
de lignes avec skip
, et enfin parcourir tout un fichier très simplement:
REM Liste des utilisateurs
for /f "eol=; skip=1 tokens=1 delims=," %%a in (data.dat) do echo %%a
Les fonctions
On peut définir des fonctions et leur passer des paramètres, on peut même faire de la récursivité en utilisant
les commandes SETLOCAL
et ENDLOCAL
en début et fin de fonction.
Par exemple, ici, on va souligner un texte passé en paramètre. Le texte est passé entre guillemets pour autoriser les espaces, et éviter au maximum les problèmes d'interprêtation liés à l'utilisation éventuelle de caractères réservés dans la chaine. Donc la boucle ci-dessous s'assure que count contient toujours du texte entre quotes. (pénibilité du langage):
:underline
SET count=%*
SET line=ÄÄ
:undloop
if %count%=="" ( echo %line%& GOTO :EOF )
SET count="%count:~1,-2%"
SET line=%line%Ä
GOTO undloop
GOTO :EOF
Noter le GOTO :EOF
qui permet de retourner à l'appelant, et l'utilisation du symbole %*
qui renvoie tous les paramètres, pour un paramètre en particulier, on utilise classiquement %1, %2, ...
ou bien si l'on souhaite supprimer les guillemets éventuels dans ces paramètres %~1, %~2, ...
Il est à souligner que le batch entier peut être appelé de l'extérieur et constitue donc une fonction. La sortie du batch retourne à l'appelant. Il est alors possible de renvoyer un code d'erreur (0 = pas d'erreur) susceptible d'être lu dans la variable %errorlevel%:
:ok
exit /B 0
:failed
exit /B 1
Les variables systèmes
Tout d'abord les chemins d'accès, ce qui est excessivement utile, notamment le dossier temporaire, le dossier des applications et celui du profil utilisateur:- %APPDATA% : chemin des données applicatives de l'utilisateur courant.
- %CommonProgramFiles% : chemin des programmes communs.
- %HOMEDRIVE% : disque local primaire (partition système).
- %HOMEPATH% : dossier par défaut pour les utilisateurs.
- %PATH% : chemins de recherche des programmes et DLL (séparés par des points virgules).
- %PATHEXT% : liste des extensions de fichier reconnus comme des exécutables.
- %TMP% et %TEMP% : répertoire temporaire de Windows.
- %ProgramFiles% : chemin des programmes.
- %SystemDrive% : disque local sur lequel le système réside.
- %SystemRoot% : chemin du système (égal à Windir).
- %USERPROFILE% : chemin du profil de l'utilisateur courant.
- %Windir% : répertoire de Windows.
Les inmanquables:
- %0 : nom du batch courant (fichier uniquement).
- %~dp0 : chemin du batch courant.
- %errorlevel% : code de sortie renvoyé par le dernier batch/éxécutable.
- %Date% : date courante.
- %Time% : heure courante.
- %CD% : répertoire courant.
Quelques informations sur la session:
- %COMPUTERNAME% : nom de l'ordinateur.
- %USERDOMAIN% : nom du domaine sous lequel l'utilisateur s'est connecté.
- %USERNAME% : nom de l'utilisateur courant.
Il y a aussi une foule d'informations sur le système:
- %COMSPEC% : nom du programme d'invite de commande.
- %LOGONSERVER% : nom du serveur de domaine.
- %NUMBER_OF_PROCESSORS% : nombre de processeurs installés.
- %OS% : nom du système d'exploitation.
- %PROCESSOR_ARCHITECTURE% : type de processeur installé.
- %PROCESSOR_IDENTIFIER% : identification du processeur installé (type, modèle, etc.).
- %PROCESSOR_LEVEL% : niveau du processeur.
- %PROCESSOR_REVISION% : révision du processeur.
- %PROMPT% : Invite affichée par l'invite de commandes.
Les commandes systèmes
Nativement le langage batch incorpore des instructions de gestion de fichiers.
if exist filename ...
if not exist filename ...
if exist directory ...
if not exist directory ...
rem se déplace dans un dossier (aller simple)
cd directory
rem se déplacer sur un autre lecteur (aller simple)
cd /d directory
rem se déplace dans un autre dossier en mémorisant le parent
pushd directory
rem retour au parent
popd
Un certain nombre de commandes externes sont par ailleurs disponibles nativement sous Windows.
- Comparaison de fichiers
fc /l /n "source.src" "dest.dst" > nul if %errorlevel% neq 0 goto differents exit :differents echo il y a des differences, les voici fc /l /n "source.src" "dest.dst"
- Rechercher une chaine dans un fichier
findstr "form_build_id" "%TEMP%\browse.htm"
- Déplacer un fichier
move "%TEMP%\browse.htm" "destination.htm"
- Renommer un/des fichier
rename "%TEMP%\snapshot_*.*" "snapshot_*.old"
- Afficher le contenu d'un fichier
type "%TEMP%\browse.htm"
- Afficher le contenu d'un fichier page par page
more "%TEMP%\browse.htm"
- Editer un fichier
edit "%TEMP%\browse.htm"
Souvent utiles, téléchargeables gratuitement:
- wget télécharger des fichiers sur internet
- gzip flux de compression
- xd convertisseur binaire hexa
- md5 calcul de checksum