it-swarm.com.de

Variablen in Batch-Datei werden nicht gesetzt, wenn sie sich innerhalb von IF befinden?

Ich habe zwei Beispiele für sehr einfache Batch-Dateien:

Einer Variablen einen Wert zuweisen:

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

Was wie erwartet zu Folgendem führt:

FOO: 1 
Press any key to continue . . .

Wenn ich jedoch dieselben zwei Zeilen in einen IF NOT DEFINED-Block einfüge:

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

Dies führt unerwartet zu:

FOO: 
Press any key to continue . . .

Dies sollte nichts mit der IF zu tun haben , da der Block offensichtlich gerade ausgeführt wird. Wenn ich BAR über if definiere, wird erwartungsgemäß nur der Text des Befehls PAUSE angezeigt.

Was gibt?


Folgefrage: Gibt es eine Möglichkeit, eine verzögerte Expansion ohne setlocal zu ermöglichen?

Wenn ich diese einfache Beispiel-Batch-Datei von einer anderen Seite aus aufrufen würde, würde das Beispiel FOO setzen, aber nur LOCALLY.

Zum Beispiel:

testcaller.bat

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

test.bat

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

Dies zeigt an:

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

In diesem Fall muss ich anscheinend die verzögerte Erweiterung im CALLER aktivieren, was ein Problem sein kann.

67
Brown

Umgebungsvariablen in Stapeldateien werden erweitert, wenn eine Zeile analysiert wird. Bei durch Klammern getrennten Blöcken (als Ihr if defined) zählt der gesamte Block als "Zeile" oder Befehl.

Dies bedeutet, dass alle Vorkommen von% FOO% durch ihre Werte ersetzt werden, bevor der Block ausgeführt wird. In deinem Fall mit nichts, da die Variable noch keinen Wert hat.

Um dies zu lösen, können Sie die verzögerte Erweiterung aktivieren :

setlocal enabledelayedexpansion

Eine verzögerte Erweiterung bewirkt, dass durch Ausrufezeichen (!) begrenzte Variablen bei der Ausführung ausgewertet werden, anstatt sie zu analysieren, wodurch das richtige Verhalten in Ihrem Fall sichergestellt wird:

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

help set gibt auch dies an:

Schließlich wurde die Unterstützung für die verzögerte Erweiterung von Umgebungsvariablen hinzugefügt. Diese Unterstützung ist standardmäßig immer deaktiviert, kann jedoch über die Befehlszeilenoption /V auf CMD.EXE aktiviert/deaktiviert werden. Siehe CMD /?

Die verzögerte Erweiterung von Umgebungsvariablen ist hilfreich, um die Einschränkungen der aktuellen Erweiterung zu umgehen, die beim Lesen einer Textzeile und nicht beim Ausführen auftreten. Das folgende Beispiel zeigt das Problem mit der sofortigen Variablenerweiterung:

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

würde die Nachricht niemals anzeigen, da der %VAR% in beiden IF -Anweisungen beim Lesen der ersten IF-Anweisung ersetzt wird, da er logisch den Hauptteil der IF enthält , was eine zusammengesetzte Aussage ist. Das IF in der zusammengesetzten Anweisung vergleicht also wirklich "Vorher" mit "Nachher", was niemals gleich sein wird. In ähnlicher Weise funktioniert das folgende Beispiel nicht wie erwartet:

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

, dass es keine Liste der Dateien im aktuellen Verzeichnis erstellt , sondern lediglich die Variable LIST auf die zuletzt gefundene Datei setzt. Dies liegt wiederum daran, dass der %LIST% nur einmal erweitert wird, wenn die Anweisung FOR gelesen wird, und zu diesem Zeitpunkt die Variable LIST leer ist. Die eigentliche FOR-Schleife, die wir ausführen, lautet also:

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

wobei LIST immer auf die zuletzt gefundene Datei gesetzt wird.

Mit der verzögerten Erweiterung von Umgebungsvariablen können Sie zur Ausführungszeit Umgebungsvariablen mit einem anderen Zeichen (dem Ausrufezeichen) erweitern. Wenn die verzögerte Variablenerweiterung aktiviert ist, können die obigen Beispiele wie folgt geschrieben werden, um wie beabsichtigt zu funktionieren:

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%
81
Joey

Das gleiche Verhalten tritt auch auf, wenn sich die Befehle in einer einzelnen Zeile befinden (& ist das Befehlstrennzeichen):

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

Joeys Erklärung ist meine Lieblingserklärung. Beachten Sie jedoch, dass enabledelayedexpansion unter Windows NT 4.0 nicht funktioniert (und ich bin mir bei Windows 2000 nicht sicher).

Nein, es ist nicht möglich, EnableDelayedExpansion ohne setlocal zu beantworten. Das ursprüngliche Verhalten, mit dem Sie konfrontiert wurden, kann jedoch verwendet werden, um dieses zweite Problem zu umgehen: Der Trick besteht darin, in derselben Zeile endlocal die Werte der von Ihnen benötigten Variablen erneut festzulegen.

Hier ist Ihr test.bat geändert:

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

Hier ist eine andere Problemumgehung: Verwenden Sie eine Prozedur in derselben Datei anstelle eines Inline-Blocks oder einer externen Datei.

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

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

Wenn dies nicht der Fall ist, haben Sie wahrscheinlich die Erweiterung der Umgebungsvariablen verzögert. Sie können es entweder mit cmd /V:OFF ausschalten oder Ausrufezeichen in Ihrem if verwenden:

@echo off
IF NOT DEFINED BAR (
    set FOO=1
    echo FOO: !FOO!
)
pause
echo on
3
John T

Dies geschieht, weil Ihre Zeile mit dem Befehl FOR nur einmal ausgewertet wird. Sie brauchen eine Möglichkeit, es neu zu bewerten. Sie können eine verzögerte Erweiterung mit dem Befehl CALL simulieren:

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

Es ist erwähnenswert, dass es funktioniert, wenn es ein einzelner Befehl in derselben Zeile ist.

z.B.

   if not defined BAR set FOO=1

wird tatsächlich FOO auf 1 setzen. Sie könnten dann eine weitere Überprüfung durchführen, wie zum Beispiel:

   if not defined BAR echo FOO: %FOO%

Hey, ich habe nie gesagt, dass es hübsch ist. Wenn Sie sich jedoch nicht mit Setlocal oder dem verzögerten Expansionsgeschäft herumschlagen möchten, können Sie dies schnell umgehen.

0
demoncodemonkey

Manchmal, wie ich es in meinem Fall getan habe, benötigen Sie möglicherweise eine alternative Lösung, wenn Sie mit älteren Systemen wie alten NT4-Servern kompatibel sein müssen, auf denen eine verzögerte Erweiterung nicht funktioniert oder aus irgendeinem Grund nicht funktioniert.

Falls Sie die Delayd-Erweiterung verwenden, wird auch empfohlen, mindestens den Check-in-Batch einzuschließen:

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

Für den Fall, dass Sie einfachere und übersichtlichere Vorgehensweisen wünschen, finden Sie hier alternative Lösungen:

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

was so funktioniert:

>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

und noch eine reine cmd-lösung:

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

es funktioniert so:

>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

und das ist voll, wenn dann andere Syntax-Lösung:

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%

es funktioniert so:

>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