Accueil HGL (général) Articles de blog Articles de blog (filtre : programmation)  

Hugoland - programmation (archives)

Utiliser des variables dans FOR

Première publication : 2016
Salut,
voici la partie 4 de mes tutos sur FOR

I. Le paradoxe de la %variable%
Contrairement à ce que certains font pour éviter les setlocal, en mettant des call, je déteste cette façon de faire car ce n'est pas joli de voir une boucle for de ce style :

for /f "tokens=1,* delims=:" %%A in ('type fichier.txt') do (
	call :lecture
	call :traitement
	for /f %%C in ('type %%B.txt') do (
		call :extraire
		call :terminer
		call :supprimer
	)
echo traitement fini pour %%A.txt
)

Et cela complique la lecture du code, qui se fait comme le traitement, à savoir 'ligne par ligne', le principe du batch.
Niveau performances et poids de fichier, quand on utilise les variables directement dans un FOR, on économise 3 lignes :
dans le FOR

call :truc

en dehors du FOR, on déclare la 'fonction' nommée "truc"

:truc

à la fin de la fonction, on déclare la fin et le retour au for

goto :eof

Tout ça par ce que ce code là ne marchera pas :

for /f "tokens=1,* delims=:" %%A in ('type fichier.txt') do (
	set variable_sortie_%%A=%prefixe%_%%B_%suffixe%
	set /a tour= %tour% + 1
	set /a nombre= %tour% - %variable% + %unautrenombre%
	echo %nombre%
)

Alors quelle solution?
Utilisons setlocal!

Code (compliqué) à savoir :
setlocal enabledelayedexpansion

Pour une raison inconnue, on ne peut pas utiliser le schéma %variable% dans une boucle for. On doit utiliser !variable! c'est bizarre, mais si on le fait pas, ça marchera jamais. Mais il faut commencer par mettre le setlocal comme indiqué ci dessus.

Pour résumer,
Pour pouvoir utiliser une %variable% dans un for, on doit utiliser !variable! → pour pouvoir utiliser !variable!, on doit mettre setlocal avant la boucle for...
Comme quoi le batch peut être pénible parfois.
On peut mettre une bonne fois pour toute cette ligne en début de code, pas besoin de remettre avant chaque boucle.
La pratique maintenant!
Je vais à nouveau utiliser des murs, car c'est bien!
Le mur est un fichier. On cherche à calculer quelques additions.
Voici comment se résente chaque ligne du fichier (revoir si besoin la partie 1 de cette série de tutos)

Si je reprends la façon de montrer les exemples dans la partie 1 de la série, nous avons ici 4 briques séparées par un "ciment" qui sera ici un ":".
Voici le fichier :

1:1:+:6
2:8:-:1
3:14:/:2

Pour s'imaginer les briques, exemple de la ligne 3 du fichier

Et la commande for :
NOTE : pour ce tuto, je vais prendre le temps de déclarer inutilement des variables, y'a bien plus simple comme mettre les %%A directement dans le set/a, mais comme cela on n'utiliserait trop peu le style !variable! et donc ce serait un code hors sujet.

@echo off
setlocal enabledelayedexpansion
for /f "tokens=1,2,3,4 delims=:" %%A in ('type fichier.txt') do (
	set nb_calcul=%%A
	set terme_1=%%B
	set calcul=%%C
	set terme_2=%%D

	set /a resultat= !terme_1! !calcul! !terme_2!

	echo Le calcul !nb_calcul! est !terme_1! !calcul! !terme_2!, ce qui fait !resultat!
)
pause

Ce qui donne en français :
Pour (for) toutes les lignes du fichier.txt (on utilise 'type' pour que FOR lise et traite le fichier ligne par ligne), on définit les variables et on calcule.
Dans ce code, le batch va ainsi exploser 3 rangées de briques et ainsi réaliser les opérations
1+6
8-1
14/2
Qui donnent évidemment toutes 7.
Vérifions :

CQFD

Ce qui évident, c'est que vu que les variables ont été définies dans le for avec par exemple "set nb_calcul=%%A", on aurait pu faire le traitement des calculs une fois sortis de la boucle... C'est donc un mauvais exemple, mais vous aurez forcément un jour besoin d'utiliser une variable tout en étant à l'intérieur de for.
On a pu réviser la façon dont on "divise" les chaînes de caractères (ici, on a eu une chaîne = une ligne de fichier.txt). Les variables étaient exprimées sous la forme !variable!.
Conclusion :
Si on veut calculer, traiter, afficher ou passer une variable en argument d'une commande en étant dans un for, on utilisera le bon setlocal et la bonne syntaxe !variable! au lieu de %variable%.

II. La sortie des variables
Si on veut conserver des variables en sortie de for, il est indispensable de les SET.
Les vanables type %%A et autres sont uniquement dans FOR, et disparaissent à la fin.
Ce code ne fonctionnera pas :

for /f %%A in ('type fichier.txt') do (
)
echo %%A
pause

Ceci est absurde. Pour cela, on fera

for /f %%A in ('type fichier.txt') do (
echo Chaîne stockée dans la variable!
set variable=%%A
)
echo %variable%
pause

Bien sûr cet exemple est absurde car on affiche directement la variable après FOR, alors qu'on pourrait tout simplement faire "echo %%A" à condition d'être encore dans FOR. Mais imaginez que vous ayez des tas de choses à exécuter avant d'afficher cette variable! Pas la peine de le faire dans un FOR!

III. Exercice
Créez un code qui inclut un FOR dans un FOR !
Il y a plein de possibilités dans cet exercice, donc pas de correction, vous verrez qu'en ayant besoin de faire une succession de plusieurs FOR utilisant des variables d'un 'FOR principal', ce tuto prend tout son sens!

Notes 2025 :

Le tutoriel ci-dessus a été écrit vers mes 16 ans sur un forum désormais inaccessible. Je l'ai laissé ici tel que je l'avais écrit à l'époque. Concernant le premier code du chapitre et le commentaire que j'en faisais : jusqu'à il y a quelques années, j'avais des avis très tranchés sur les styles de programmation, des façons de faire que je détestais voir postées sur le forum. Certes aujourd'hui je préfère toujours, quand c'est possible, utiliser SETLOCAL pour pouvoir (quasiment) tout faire directement dans la boucle FOR, mais il n'y a pas vraiment de vérité générale et stricte dans ce domaine, en cela, c'est une question de style personnel et il n'y a pas de meilleure façon de faire qu'une autre, à part si les contraintes techniques en imposent une. Par la suite, j'ai fini par être confronté à des cas très spécifiques où la solution du SETLOCAL n'était pas suffisante et où une solution par CALL était nécessaire. Il faut alors retenir du début de ce tutoriel les deux possibilités, et surtout pas que "je déteste cette façon de faire car ce n'est pas joli"... Concernant la performance, il peut être intéressant de mesurer l'impact d'un CALL vers une fonction qui réalise la même chose qu'un code mis directement dans une boucle for. Par exemple ici le batch réalise %nb% fois l'écriture vers la sortie NUL son propre code ligne par ligne. L'heure actuelle est donnée en centièmes de secondes, donc il suffit de soustraire l'heure de fin par l'heure de début pour connaître le temps que le traitement prend. @echo off setlocal enabledelayedexpansion set nb=128 pause echo TYPE dans le FOR : %time% set c=0 for /L %%A in (1,1,%nb%) do ( for /f "tokens=*" %%L in ('type "%~dpnx0"') do ( >nul echo %%L ) set /a c+=1 < nul set/p "=!c!/%nb% " ) echo. echo Fin : %time% pause echo TYPE hors du FOR : %time% set c=0 for /L %%A in (1,1,%nb%) do ( call :fx set /a c+=1 < nul set/p "=!c!/%nb% " ) echo. echo Fin : %time% pause exit :fx for /f "tokens=*" %%L in ('type "%~dpnx0"') do ( >nul echo %%L ) goto:eof Ici, avec nb=128, 128 traitements directs ont pris 29,52 - 28,57 = 0,95 seconde et 128 traitements avec CALL ont pris 32,28 - 31,14 = 1,14 seconde.