2009-12-03 20:51:48 +0000 2009-12-03 20:51:48 +0000
72
72

Les variables du fichier de lots ne sont pas définies lorsqu'elles se trouvent à l'intérieur de la FI ?

J'ai deux exemples de fichiers de lots très simples :

Assigner une valeur à une variable :

@echo off
set FOO=1
echo FOO: %FOO%
pause
echo on

Ce qui, comme prévu, donne

FOO: 1 
Press any key to continue . . .

Cependant, si je place les deux mêmes lignes à l'intérieur d'un bloc SI NON DÉFINI :

@echo off
IF NOT DEFINED BAR (
    set FOO=1
    echo FOO: %FOO%
)
pause
echo on

Ce qui, comme prévu, donne :

Ce qui, comme prévu, donne :

:

FOO: 
Press any key to continue . . .

Cela ne devrait pas avoir de rapport avec la FI, il est clair que le bloc est en cours d'exécution. Si je définis BAR au-dessus du if, seul le texte de la commande PAUSE est affiché, comme prévu.

Qu'est-ce que ça donne ?


Question de suivi : Y a-t-il un moyen de permettre une expansion retardée sans setlocal ?

Si je devais appeler ce simple exemple de fichier batch de l'intérieur d'un autre, l'exemple définit FOO, mais seulement LOCAL.

Par exemple :

testcaller.bat

@call test.bat 
@echo FOO: %FOO% 
@pause

test.bat

@setlocal EnableDelayedExpansion 
@IF NOT DEFINED BAR ( 
    @set FOO=1 
    @echo FOO: !FOO! 
)

Cela s'affiche :

FOO: 1 
FOO: 
Press any key to continue . . .

Dans ce cas, il semble que je doive activer l'expansion retardée dans l'APPEL, ce qui peut être un problème.

Réponses (6)

86
86
86
2009-12-03 21:35:26 +0000

Les variables d'environnement dans les fichiers batch sont développées lorsqu'une ligne est analysée. Dans le cas de blocs délimités par des parenthèses (comme votre if defined), le bloc entier compte comme une “ligne” ou une commande.

Cela signifie que toutes les occurrences de %FOO% sont remplacées par leurs valeurs avant l'exécution du bloc. Dans votre cas avec rien, car la variable n'a pas encore de valeur.

Pour résoudre ce problème, vous pouvez activer l'expansion retardée :

setlocal enabledelayedexpansion

L'expansion retardée fait que les variables délimitées par des points d'exclamation (!) sont évaluées à l'exécution au lieu d'être analysées, ce qui garantira le comportement correct dans votre cas :

if not defined BAR (
    set FOO=1
    echo Foo: !FOO!
)

help set détaille cela aussi :

Enfin, la prise en charge de l'expansion retardée des variables d'environnement a été ajoutée. Cette prise en charge est toujours désactivée par défaut, mais peut être activée/désactivée via le passage de la ligne de commande /V à CMD.EXE. Voir CMD /?

L'expansion retardée des variables d'environnement est utile pour contourner les limites de l'expansion actuelle qui se produit lorsqu'une ligne de texte est lue, et non lorsqu'elle est exécutée. L'exemple suivant illustre le problème de l'expansion immédiate de la variable :

set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" @echo If you see this, it worked
)

n'affichera jamais le message, puisque la valeur %VAR% dans les deux instructions IF est substituée à la lecture de la première instruction IF, puisqu'elle inclut logiquement le corps de la valeur IF, qui est une instruction composée. Ainsi, le IF à l'intérieur de l'instruction composée compare réellement “avant” avec “après” qui ne seront jamais identiques. De même, l'exemple suivant ne fonctionnera pas comme prévu :

set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%

en ce sens qu'il n'établira pas une liste de fichiers dans le répertoire courant, mais se contentera de placer la variable LIST sur le dernier fichier trouvé. Encore une fois, cela s'explique par le fait que la variable %LIST% n'est développée qu'une seule fois lorsque l'instruction FOR est lue, et qu'à ce moment-là, la variable LIST est vide. Ainsi, la boucle FOR que nous exécutons est la suivante :

for %i in (*) do set LIST= %i

qui ne fait que placer la variable LIST sur le dernier fichier trouvé.

L'expansion retardée des variables d'environnement vous permet d'utiliser un caractère différent (le point d'exclamation) pour étendre les variables d'environnement au moment de l'exécution. Si l'expansion retardée des variables est activée, les exemples ci-dessus pourraient être écrits comme suit pour fonctionner comme prévu :

set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "!VAR!" == "after" @echo If you see this, it worked
)

set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%
4
4
4
2011-03-26 16:26:14 +0000
3
3
3
2009-12-03 21:02:01 +0000

Si cela ne fonctionne pas de cette façon, vous avez probablement retardé l'expansion des variables d'environnement. Vous pouvez soit l'éteindre avec cmd /V:OFF soit utiliser des points d'exclamation à l'intérieur de votre si :

@echo off
IF NOT DEFINED BAR (
    set FOO=1
    echo FOO: !FOO!
)
pause
echo on
1
1
1
2013-11-27 11:52:14 +0000

Cela se produit parce que votre ligne avec la commande FOR n'est évaluée qu'une seule fois. Vous devez trouver un moyen de la réévaluer. Vous pourriez simuler une expansion retardée avec CALL command :

for /l %%I in (0,1,5) do call echo %%RANDOM%%
0
0
0
2016-12-28 21:46:34 +0000

Parfois, comme dans mon cas, lorsque vous avez besoin d'être compatible avec des systèmes existants comme les anciens serveurs NT4 dont l'extension retardée ne fonctionne pas ou ne fonctionne pas pour une raison quelconque, vous pouvez avoir besoin d'une autre solution.

Dans le cas où vous utiliserez l'expansion retardée, il est également recommandé d'inclure au moins un contrôle par lot :

IF NOT %Comspec%==!Comspec! ECHO Delayed Expansion is NOT enabled.

Au cas où vous souhaiteriez simplement une approche plus lisible et plus simple, voici des solutions alternatives :

REM Option 2: use `call` inside block statement
IF NOT DEFINED BAR2 ( 
  set FOO2=2 
  call echo FOO2: %%FOO2%%
)
echo FOO2: %FOO2%

qui fonctionne comme suit :

>REM Option 2: use `call` inside block statement

>IF NOT DEFINED BAR2 (
 set FOO2=2
 call echo FOO2: %FOO2%
)
FOO2: 2

>echo FOO2: 2
FOO2: 2

et une autre solution de cmd pur :

REM Option 3: use `goto` logic blocks
IF DEFINED BAR3 goto endbar3
  set FOO3=3 
  echo FOO3: %FOO3%
:endbar3
echo FOO3: %FOO3%

qui fonctionne comme ceci :

>REM Option 3: use `goto` logic blocks

>IF DEFINED BAR3 goto endbar3

>set FOO3=3

>echo FOO3: 3
FOO3: 3

>echo FOO3: 3
FOO3: 3

et c'est la solution syntaxique complète SI ALORS :

REM Full IF THEN ELSE solution
IF NOT DEFINED BAR4 goto elsebar4
REM this is TRUE condition, normal batch flow
  set FOO4=6
  echo FOO4: %FOO4%
  goto endbar4
:elsebar4
  set FOO4=4
  echo FOO4: %FOO4%
  set BAR4=4
:endbar4
echo FOO4: %FOO4%
echo BAR4: %BAR4%

ça marche comme ça :

>REM Full IF THEN ELSE solution

>IF NOT DEFINED BAR4 goto elsebar4

>set FOO4=4

>echo FOO4: 4
FOO4: 4

>set BAR4=4

>echo FOO4: 4
FOO4: 4

>echo BAR4: 4
BAR4: 4
0
0
0
2012-06-26 15:36:32 +0000

Il est à noter que cela fonctionne si c'est une seule commande sur la même ligne.

par exemple,

if not defined BAR set FOO=1

va en fait mettre FOO à 1. Vous pouvez alors faire une autre vérification comme

if not defined BAR echo FOO: %FOO%

Hé, je n'ai jamais dit que c'était joli. Mais si vous ne voulez pas vous frotter à setlocal ou à l'activité d'expansion retardée, c'est une solution rapide.