neonzero
Goto Top

Batch - for-Schleife vorzeitig beenden

Hallo.

Ich möchte eine for-Schleife vorzeitig beenden und suche nach Ideen dafür. Hier der Code, der verdeutlichen soll, was ich meine:
for %%a in (a b c d e) do ( 
  echo %%a
  if "%%a" == "c" break_loop  
)
-------------------------------------------
gewünschte Ausgabe:	
a
b
c
Lösung 1: Eine mögliche Lösung wäre der folgende Code:
::Der folgende Code wurde nach einem Hinweis von bastla (siehe seinen Beitrag weiter unten)
::derart angepasst, dass hier auf setlocal EnableDelayedExpansion verzichtet werden kann. Thx

for %%a in (a b c d e) do if not defined _break (
  echo %%a
  if "%%a" == "c" set _break=now  
)
Dieser Ansatz hat den Nachteil, dass d und e weiterhin durchlaufen werden, auch wenn der Block dabei nicht abgearbeitet wird. Will man mithilfe einer solchen for-Schleife z.B. eine bestimmte Information aus einer großen Datei holen, so werden – nachdem man die Information gefunden hat – auch die darauf folgenden Zeilen eingelesen. Und für jede Zeile wird die if-Abfrage abgearbeitet, was je nach Größe der Datei unnötig viel Zeit in Anspruch nimmt.

Lösung 2: Das zuvor beschriebene Problem lässt sich mit dem folgenden Code umgehen:
call :FncMyForLoop
goto :EOF

:FncMyForLoop
  for %%a in (a b c d e) do (
    echo %%a
    if "%%a" == "c" exit /b 0  
  )
 exit /b 1
Der Code hat gleichzeitig den Vorteil, dass man anhand des Errorlevels erkennen kann, ob „c“ gefunden wurde, oder nicht (nach dem call-Aufruf: if %errorlevel% == 1 echo c wurde nicht gefunden! ).

Diese Lösung ist allerdings sehr umständlich, da man für jede for-Schleife eine eigene Funktion schreiben müsste. Viel besser wäre es, wenn man die for-Schleife direkt zu einem Abbruch zwingen kann. Um genau solche Lösungsansätze geht es in diesem Thread.

Bye, nz
Lösung 3: Benutzer ahe hat weiter unten den folgenden Ansatz eingebracht.
for %%a in (a b c d e) do ( 
  echo %%a
  if "%%a" == "c" goto :Weiter  
)
:Weiter
echo Hier geht es weiter ...
Eine noch idealere Lösung wäre es, wenn man die for-Schleife einfach beenden könnte, ohne den Umweg über die Sprungmarke zu gehen. Andernfalls müsste man für jede dieser for-Schleifen eine eigene Sprungmarke setzen (was zugegebenermaßen ein eher kleines Übel wäre). Hat jemand dafür eine Idee? –- NeonZero 29.08.2008 18:19

Content-Key: 95668

Url: https://administrator.de/contentid/95668

Printed on: April 19, 2024 at 04:04 o'clock

Member: ahe
ahe Aug 29, 2008 at 15:36:53 (UTC)
Goto Top
Hallo,

reicht dir das vielleicht schon:

@echo off
for %%a in (a b c d e) do (  
	echo %%a 
	if "%%a" == "c" exit /b   
) 

Die Ausgabe am Prompt sieht dann so aus:
a
b
c

mfg
Axel
Member: NeonZero
NeonZero Aug 29, 2008 at 15:46:30 (UTC)
Goto Top
Hallo Axel und danke für Deine Antwort.

Die Idee ist nicht schlecht, hat nur leider den Nachteil, dass dadurch umgehend die Batch beendet wird. Ziel ist es nicht, die obige Ausgabe zu erhalten, sondern eine allgemein verwendbare Möglichkeit zu finden, die for-Schleife (und nicht die Batch) vorzeitig zu beenden. Die Zielstellung habe ich offensichtlich nicht eindeutig formuliert. Mein Fehler. Sorry.

Bye, nz
Member: ahe
ahe Aug 29, 2008 at 15:59:22 (UTC)
Goto Top
Ach so... mmh, das gehe doch zu einer Sprungmarke... danach kannst Du auch noch andere Dinge ausführen...

@echo off
for %%a in (a b c d e diedeldum gnuffz c c d d) do (  
	echo %%a 
	if "%%a" == "c" goto WEITER   
) 

:WEITER
echo es geht weiter
goto DA

:DA
echo Jetzt bin ich da

:END
echo Laeuft bis zum Ende
echo.
echo und tschuess...

mfg
Axel
Member: NeonZero
NeonZero Aug 29, 2008 at 16:22:46 (UTC)
Goto Top
Ein schöner Ansatz. Danke. Ich habe ihn oben als Nachtrag aufgelistet.

Bye, nz
Member: bastla
bastla Aug 29, 2008, updated at Oct 18, 2012 at 16:36:10 (UTC)
Goto Top
Hallo NeonZero!

Eine etwas abgewandelte Version Deiner ersten Variante kommt ohne "delayedExpansion" aus:
@echo off & setlocal
set done=
for %%a in (a b c d e) do if not defined done (
  echo %%a
  if "%%a"=="c" set done=break  
)
Einfach aus der Schleife herauszuspringen ist zwar auch möglich, aber von Biber nicht empfohlen ...

Grüße
bastla
Member: NeonZero
NeonZero Aug 29, 2008 at 19:12:20 (UTC)
Goto Top
Hallo bastla.

Das ist ein guter Vorschlag. Ich habe den Code oben mit einem Hinweis auf Deinen Beitrag entsprechend angepaßt. Danke.

Zum verlinkten Beitrag: " Das "Verlassen" einer FOR-Anweisung ist eigentlich vom sympathischen Weltmarktführer nicht vorgesehen, geschweige denn zugesichert. " (Biber)

@Biber (falls Du mitliest): Woher stammt diese Information und mit welche Auswirkungen ist zu rechnen? Hast Du (oder sonst jemand) diesbezüglich eigene Erfahrungen gemacht?

Bye, nz
Member: bastla
bastla Aug 29, 2008 at 20:30:58 (UTC)
Goto Top
Hallo NeonZero!

Weil Du oben zur Variante mit der "Schalter"-Variable meintest:
Dieser Ansatz hat den Nachteil, dass d und e weiterhin durchlaufen werden, auch wenn der Block dabei nicht abgearbeitet wird. Will man mithilfe einer solchen for-Schleife z.B. eine bestimmte Information aus einer großen Datei holen, so werden – nachdem man die Information gefunden hat – auch die darauf folgenden Zeilen eingelesen. Und für jede Zeile wird die if-Abfrage abgearbeitet, was je nach Größe der Datei unnötig viel Zeit in Anspruch nimmt.
Um eine bestimmte Information aus einer großen Datei zu holen, wird sich oftmals "findstr" anbieten, etwa:
for /f "delims=" %%i in ('findstr /c:"bestimmte Information" "D:\Große Datei.txt"') do ...  
Je nach Verwendungszweck kann es dann noch sinnvoll sein, die Zeilennummern (für einen späteren "direkten" Zugriff) mit angeben zu lassen ("/n") und durch "Hintereinanderschalten" mehrerer Suchvorgänge die Anzahl der tatsächlich per Schleife zu bearbeitenden Zeilen zu reduzieren ...

Grüße
bastla
Member: Biber
Biber Aug 29, 2008 at 23:50:19 (UTC)
Goto Top
Moin NeonZero,

zu dem nicht empfohlenen (und nicht vorgesehenen) Verlassen einer FOR-Schleife.
M$ hat in den (relativ umfangreichen) Dokumentationen zu FOR und CALL und GOTO nie erwähnt, ob und welche der FOR-Anweisungen gefahrlos unvollendet bleiben können.
Wir reden doch hier (nur zur Erinnerung) von einem Befehlsinterpreter namens CMD.exe, der eigentlich jeden vom CMD-Prompt abgesetzten Befehl xy (oder auch einen Batch abc.bat) am Dienstag um 13h exakt so interpretieren sollte wie am Freitag um 04:23h.
Aber schon durch einen Batch-Programmabbruch (z.B. ein Syntaxfehler) in einem Batch mit einigen der "neueren" Sprachelemente wie
- Setlocal/Endlocal
- EnableExtensions/EnableDelayedExpansion bzw. der Disable-Gegenstücke...
... merkst Du sehr schnell, dass die CMD.exe sehr empfindlich auf geöffnete und nicht geschlossene Klammern (das sind diese Elemnte ja) reagiert.
Plötzlich befindest Du Dich -aus eimem Batch herausgeflogen- am CMD-Prompt und das "Echo ist OFF" - der häufigste und harmloseste Fall.
Oder aber Du bist am CMD-Prompt in einem Zustand, in dem Du lokale (setlocal) Variablen anlegen kannst - was eigentlich nur im Batch und nie am CMD-Prompt gehen sollte.

Nun sind seit W2000 noch ein paar lustige FOR-Anweisungsvarianten dazugekommen - zur "schon immer" vorhandenden For-ein-paar-Elemente-durchwackel-Anweisung sind rekursive Konstrukte (FOR /R) und vor allem FOR-Anweisungen dazugekommen, die Files/Filehandles/Verzeichnisstrukturen/Fremdfunktionen nutzen/öffnen/anfordern.
Was passiert denn mit einer für eine For/F-Anweisung zum Lesen geöffneten Datei, wenn das Lesen/die FOR-Anweisung niemals endet? Was passiert in einer rekursiv angelegten FOR/R-Anweisung, wenn Du das Wurzelelement löscht? Oder in einer FOR/D-Anweisung? Wie viele Stacks/Caches/Buffers/Pushs und Pops hat denn die CMD.exe reserviert oder allokiert und wieviele freigegeben bei einer unvollendeten Schleife?

Das mag testen wer will, aber in Batchabläufen, die im produktiven Umfeld laufen (z.B. zur Datensicherung oder Update-Gewährleistung) möchte ich NICHTS riskieren, das Seiteneffekte hervorrufen könnte. Oder eben dazu führt, dass sich ein und derselbe Batch je nachdem was vorher so gelaufen ist, am Dienstag um 13h anders verhält als Freitags morgens.
...ich verhalte mich auch montags nicht anders as mittwochs...

Grüße
Biber
Member: NeonZero
NeonZero Aug 30, 2008 at 07:27:41 (UTC)
Goto Top
Hallo Biber.

Das, was Du beschrieben hast, ist ein grundsätzliches Problem. Wenn mir Konsole-Programme, die in C geschrieben wurden, während der Entwicklung abgestürzt sind, gab es ähnliche Effekte, gerade was das Environment anbelangt. Diese traten aber nie auf, wenn das Programm durch lief.

Ein für die cmd.exe unerwarteter Absturz einer Batch oder eines beliebigen Konsoleprogramms kann schlimme Auswirkungen haben. Denn die Konsole besteht danach weiter. Sie ist so rudimentär, dass sie – anders als bei einer Windows-Applikation – hierfür keine Schutzmechanismen anbietet (einmal abgesehen von konzeptionellen Ansätzen wie start, oder setlocal, die die Arbeitsweise einschränken oder nur bedingt „schützen“ können). Ein Konsoleprogramm, das danach in derselben Konsole aufgerufen wird, läuft daher in einer nahezu undefinierten Umgebung. Deshalb ist es hier umso wichtiger, Programmierfehler zu vermeiden, die einen Absturz zur Folge haben könnten. Das gilt gleichermaßen für Batches, C-Programme, oder was auch immer.

Verhindern kann man dieses Verhalten, indem man start verwendet, also jedes Konsoleprogramm in einer eigenen cmd.exe-Umgebung ausführt. Dann muss man sich aber darüber im Klaren sein, das die Konsoleprogramme so keinen Einfluss mehr auf die Umgebung des Aufrufers nehmen können (auch eine Batch ist dann nicht mehr in der Lage, eine Variable der ursprünglichen Konsole direkt zu ändern).

Wenn MS die Stack-Behandlung der cmd.exe nicht vollkommen verbockt hat, sollten auch bei Batches sämtliche Sprünge aus Schleifen / Funktionen / Blöcken, gleich welcher Art, korrekt abgearbeitet und zum Ende des Programms sauber abgeschlossen werden. Das bedeutet z.B. auch, dass die oben erfragte Datei, die in der for-Schleife geöffnet wurde, ordentlich geschlossen wird (inkl. Freigabe sämtlicher Handle, etc.), sobald die Batch ein reguläres Ende findet. Dann nämlich werden auch sämtliche for-Schleifen, aus die man herausgesprungen ist, regulär beendet (so sollte es zumindest sein).

Daher nun meine Frage, ob solche Probleme auch dann beobachtet wurden, wenn die Batch nicht an einem für die cmd.exe unerwarteten Punkt abgestürzt ist. Denn wenn man Probleme mit dem Environment hat, obgleich die Batch ohne Programmierfehler durch lief, ist das ein sicheres Zeichen dafür, dass die cmd.exe tatsächlich unsauber arbeitet und solche Sprünge nicht verkraftet.

Bye, nz
Member: tom1stein
tom1stein Jan 12, 2018 updated at 13:38:05 (UTC)
Goto Top
Microsoft beschreibt nirgends, dass eine for-Schleife nicht mit einem GOTO abgebrochen werden darf. Man kann also annehmen, dass dies erlaubt ist. Tatsächlich scheint intern der Interpreter sauber aufzuräumen, jedenfalls hatte ich mit einem reinen Abbruch noch nie Probleme auch in komplexen Scripten. Und undefinierte Ängste sind eh ein Hinweis-, aber kein Ratgeber. Erlaubt ist also:

for (irgendetwas) do (
  for (irgendetwasanderes) do (
    goto Gefunden
  )
)
:Gefunden

Diese meine Einschätzung wird gestützt durch die Tatsache, dass Sprünge, die nicht stumpf alle Ebenen verlassen, explizit abgefangen werden. Das folgende führt noch vor der echo-bedingten Anzeige zu einem '")" kann syntaktisch an dieser Stelle nicht verarbeitet werden.':

echo on
for (irgendetwas) do (
  for (irgendetwasanderes) do (
    goto Absturz
  )
:Absturz      << Diese Zeile darf nicht hier stehen
)

Tom
Member: ahe
ahe Jan 15, 2018 at 16:47:29 (UTC)
Goto Top
Na ja, MS hat schon am Interpreter in den letzten Jahren deutlich gedreht, manche Einstellungen verändert, manche Parameter bei Befehlen hinzugefügt, etc., so dass manche Skripte nicht mehr so laufen, wie noch vor 10 Jahren...
(wenn ich alleine an die Datum/Uhrzeit Verarbeitung oder sprachspez. Dinge denke...)

Eigentlich wird auch immer mehr der Fokus auf die Powershell gelegt, als in den "alten" Kommandointerpreter... der wird nur noch notgedrungen, vermutlich aus Kompatibilitätsgründen, mitgezogen...


mfg
Axel


. . . da fühlt man sich doch tatsächlich wieder jünger, wenn man seine alten Beiträge nach ca. 10 Jahren mal wieder sieht... face-smile

Ist fast wie ein Schulfreund nach 20-30 Jahren zu sehen und soger wiederzuerkennen...