85 votes

Les variables du fichier batch ne sont pas définies lorsqu'elles sont dans IF ?

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

Attribution d'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 IF NOT DEFINED :

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

Este de façon inattendue résulte en :

FOO: 
Press any key to continue . . .

Este ne devrait pas n'ont rien à voir avec l'IF, 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 qui se passe ?


Question de suivi : Existe-t-il un moyen d'activer l'expansion différée sans setlocal ?

Si j'appelle cet exemple simple de fichier batch à partir d'un autre fichier, l'exemple définit FOO, mais seulement LOCALEMENT.

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

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

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

102voto

davidcann Points 1005

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 fichier 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 le bloc est exécuté. Dans votre cas, sans rien, puisque la variable n'a pas encore de valeur.

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

setlocal enabledelayedexpansion

L'expansion retardée provoque l'apparition de variables délimitées par des points d'exclamation ( ! ) à évaluer lors de l'exécution au lieu de l'analyse syntaxique, ce qui garantira un comportement correct dans votre cas :

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

help set détails cela aussi :

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

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

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

n'afficherait jamais le message, puisque le site %VAR% en les deux IF Les déclarations sont est substituée lorsque la première instruction IF est lue, puisqu'elle inclut logiquement inclut le corps de l'instruction IF qui est un énoncé composé. Donc le IF dans l'instruction composée compare réellement "avant" et "après". qui ne seront jamais égaux. De même, l'exemple suivant ne fonctionnera pas comme comme prévu :

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

en ce sens qu'il pas établir une liste de dans le répertoire courant, mais se contentera de définir la valeur LIST au dernier fichier trouvé. Encore une fois, c'est parce que la variable %LIST% est développé une seule fois lorsque le FOR La déclaration est lue, et à ce moment-là le site LIST est vide. Ainsi, le réel boucle for que nous exécutons est :

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

qui ne cesse de s'installer LIST au dernier fichier trouvé.

Expansion retardée des variables d'environnement vous permet d'utiliser un caractère caractère (le point d'exclamation) pour l'expansion des variables d'environnement au d'environnement au moment de l'exécution. Si l'expansion différée des variables est activée, les exemples ci-dessus 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%

4voto

dolmen Points 1175

Le même comportement se produit également lorsque les commandes sont sur une seule ligne ( & est le séparateur de commande) :

if not defined BAR set FOO=1& echo FOO: %FOO%

L'explication de Joey est ma préférée. Notez cependant que enabledelayedexpansion ne fonctionne pas sous Windows NT 4.0 (et je ne suis pas sûr pour Windows 2000).

Pour ce qui est de votre question complémentaire, non, il n'est pas possible de EnableDelayedExpansion sans setlocal . Cependant, le comportement original qui vous a été défavorable peut être utilisé pour résoudre le second problème : l'astuce consiste à endlocal sur la même ligne où vous définissez à nouveau les valeurs des variables dont vous avez besoin.

Voici votre test.bat modifié :

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

Mais voici une autre solution à ce problème : utiliser une procédure dans le même fichier au lieu d'un bloc en ligne ou d'un fichier externe.

@echo off
if not defined BAR call :NotDefined
pause
goto :EOF

:NotDefined
set FOO=1
echo FOO: %FOO%
goto :EOF

3voto

Dalroth Points 2468

Si cela ne fonctionne pas de cette façon, vous avez probablement une extension de variable d'environnement retardée. Vous pouvez soit la désactiver avec cmd /V:OFF ou 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

1voto

Free Consulting Points 280

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

for /l %%I in (0,1,5) do call echo %%RANDOM%%

0voto

Tomalak Points 1595

Il est intéressant de noter que cela fonctionne VRAIMENT s'il s'agit d'une seule commande sur la même ligne.

par exemple

   if not defined BAR set FOO=1

va en fait fixer FOO à 1. Vous pourriez alors faire une autre vérification telle que :

   if not defined BAR echo FOO: %FOO%

Hé, je n'ai jamais dit que c'était joli. Mais si vous ne voulez pas vous embêter avec setlocal ou l'expansion retardée, c'est une solution rapide.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X