Tutorial zur FOR-Schleife
24.11.2010
20:18:11 Uhr12011 Aufrufe
10 Antworten
20:18:11 Uhr
10 Antworten
Sehr hilfreich +13
Da hier im Forum immer wieder mal die Frage nach einem Tutorial für die FOR-Schleife in Batch-Skript auftaucht und die Suche bei Google auch keine wirklich befriedigenden Ergebnisse liefert, habe ich mich nun entschlossen, selbst ein solches Tutorial zu schreiben. Ich werde mich bemühen, auf alle Details dieses komplexen Befehls einzugehen. Da ich jedoch kein geübter Verfasser solcher Tutorials bin, sind konstruktive Kritik und Verbesserungsvorschläge willkommen.
Dieses Tutorial richtet sich vor allem an interessierte Neulinge in Sachen Batchprogrammierung, denen die Hilfe zum FOR-Befehl nur ungenügende Informationen liefert. Gerade die Variante FOR /F bietet viele Möglichkeiten, die jedoch sehr knapp bzw. verwirrend beschrieben werden. Wichtige Details fehlen ganz.
Bei den Erklärungen der einzelnen Varianten des FOR-Befehls werden, wie allgemein üblich, optionale Teile in eckigen Klammern eingeschlossen dargestellt.
Bei allen Varianten wird eine Laufvariable benutzt, die entgegen der üblichen Regel für CMD-Umgebungsvariablen nur mit einem vorangestellten Prozentzeichen gekennzeichnet wird. Wird die FOR-Schleife in Batchfiles verwendet, muss dieses Prozentzeichen verdoppelt werden.
Als Variablennamen dürfen nur einzelne Zeichen verwendet werden. Zwar sind sehr viele der druckbaren ASCII-Zeichen als Variablennamen erlaubt, man sollte sich aber auf die Verwendung von Buchstaben (A-Z oder a-z) beschränken.
Bei den Variablennamen wird zwischen Groß- und Kleinschreibung unterschieden. Die Variable %I ist also etwas anderes als %i.
Möchte man mehr als einen Befehl in der FOR-Schleife ausführen lassen, kann man wie üblich die Befehle mit dem Operator & verketten oder (in Batchfiles) die Befehlszeile durch die Verwendung von Klammern auf mehrere Zeilen "verlängern":
Da diese Notation in Batchfiles verwendet wird, hier auch die Kennzeichnung der Laufvariablen durch zwei Prozentzeichen. Aber Achtung: Eine solche Konstruktion wird vom Befehlszeileninterpreter als eine Zeile betrachtet! Was bedeutet das für den Programmierer? Befehle, die mit Variablen arbeiten, die in dem geklammerten Block verändert werden, funktionieren nicht wie gewünscht. Das liegt an der Art und Weise, wie der Befehlsinterpreter Befehlszeilen auswertet, übersetzt und ausführt. Stichwort: Erweiterung von Variablen. Damit ist das Ersetzen einer Variable in einem Ausdruck durch ihren Wert gemeint.
Durch Zeile 6 wird der Text "Geben Sie den Benutzernamen ein: " angezeigt und dann auf eine Eingabe gewartet, die mit ENTER abgeschlossen werden muss. Wenn z.B. "Paul" eingegeben wurde (und nicht nur ENTER gedrückt wurde), würde man erwarten, dass der Inhalt des Verzeichnisses C:\Users\Paul angezeigt wird. Stattdessen erscheint aber der Inhalt des Verzeichnisses, das bei Start des obigen Batchfiles das aktuelle Verzeichnis war.
Die Zeilen 8 bis 11 werden vom Befehlsinterpreter als eine Zeile betrachtet. Bevor die Befehle ausgeführt werden, werden alle Variablen durch ihren aktuellen Wert ersetzt (erweitert). Da op_dir zu diesem Zeitpunkt nicht definiert ist, wird Zeile 8 als
übersetzt, was einem einfachen DIR entspricht.
Um zum gewünschten Ergebnis zu kommen, muss die verzögerte Variablenerweiterung aktiviert und benutzt werden. Das Beispiel muss dann so lauten:
In Zeile 1 wird durch ENABLEDELAYEDEXPANSION die verzögerte Variablenerweiterung aktiviert. Dadurch, dass in Zeile 10 der Variablenname op_dir in Ausrufezeichen statt in Prozentzeichen eingeschlossen ist, wird dem Befehlsinterpreter mitgeteilt, diesen Variablennamen erst bei Ausführung des DIR-Befehls durch seinen Wert zu ersetzen, der durch den vorhergehenden SET-Befehl gesetzt wurde.
Die Variante FOR /R hat einen Parameter Basisverzeichnis und die Variante FOR /F hat die Parameter SKIP=x, TOKENS=x, DELIMS=x und EOL=x. Die Werte all diese Parameter können nicht durch eine Laufvariable einer umgebenden FOR-Schleife oder durch eine verzögert erweiterte Variable (in Ausrufezeichen eingeschlossen) angegeben werden. Lediglich die Angabe als normale Umgebungsvariable (in Prozentzeichen eingeschlossen) ist möglich. Verschachtelte FOR-Schleifen werden, wie schon erwähnt, vom Kommandozeileninterpreter als eine Zeile aufgefasst. Wenn solch eine Zeile übersetzt wird, muss der Wert der Variablen, die als Wert einer der o.g. Parameter angegeben ist, schon feststehen und kann sich während der Abarbeitung der Schleifen auch nicht mehr ändern. Diese Voraussetzungen werden nur von normalen Umgebungsvariablen erfüllt.
Damit lassen sich Zeichenketten und Dateinamensmasken verarbeiten. Satz steht für eine durch Leerzeichen getrennte Folge von Zeichenketten. Enthält eine Zeichenkette selbst Leerzeichen, muss sie in Anführungszeichen eingeschlossen werden. Bei Ausführung der FOR-Schleife wird bei jedem Durchlauf eine der Zeichenketten (angefangen bei der ersten von links) an die Laufvariable zugewiesen. Ist eine Zeichenkette in Anführungszeichen eingeschlossen, werden diese auch an die Laufvariable zugewiesen. Enthält eine Zeichenkette Wildcardzeichen (? oder *), wird sie als Maske für Dateinamen aufgefasst und im angegebenen Verzeichnis oder, ohne Verzeichnisangabe, im aktuellen Verzeichnis nach Dateien gesucht, auf deren Name diese Maske passt. Danach wird pro Schleifendurchlauf in alphabetischer Reihenfolge einer dieser Dateinamen an die Laufvariable zugewiesen.
Beispiel 1 (für die Kommandozeile):
Wenn sich im aktuellen Verzeichnis die Dateien Test.txt und MeinText.txt befinden und im Verzeichnis E:\My Scripts die Dateien Backup.bat und RDir.bat, ist die Ausgabe folgende:
Wurde in Satz zusammen mit einer Dateimaske ein Pfad angegeben, wird er bei der Ausgabe auch angezeigt. Das ist auch der Vorteil gegenüber dem Befehl
Die Anführungszeichen um Dateimasken werden nicht ausgegeben. Zeichenketten und Dateimasken lassen sich gemischt angeben.
Beispiel 2:
Hier wird die FOR-Schleife benutzt, um zu prüfen, ob die Variable %Action% einen der zur Eingabe zugelassenen Buchstaben enthält. Durch den Parameter /i beim IF-Befehl sind auch die entsprechenden Kleinbuchstaben zugelassen. Je nach Inhalt von %Action% wird entweder eines der entsprechenden Labels angesprungen oder bei einer unzulässigen Eingabe die Abfrage wiederholt (GOTO :SelectAction). Die GOTO :EOF Befehle springen jeweils zum Programmende (EOF=End Of File), beenden also das Skript.
Beispiel 3 (für die Kommandozeile):
Wenn sich im Verzeichnis E:\ die Verzeichnisse My Scripts und My Docs befinden, ist die Ausgabe folgende:
Beispiel 4 (für die Kommandozeile):
Wenn sich im Verzeichnis E:\Archiv die Dateien Test1.txt und Test2.txt und das Verzeichnis Audio mit den Dateien Test1.mp3, Test2.mp3, Track01.mp3 und Track02.mp3, befinden, ist die Ausgabe folgende:
Enthält Satz lediglich einen Punkt, erhält man unter Verwendung der Erweiterung von Laufvariablen einen Ersatz für
der auch das Basisverzeichnis liefert. Unter DOS/Windows gibt es nämlich in jedem Verzeichnis zwei Standard-Einträge, die immer vorhanden sind: . und .. Der Punkt (.) stellt einen Verweis auf das Verzeichnis selbst dar. Der zweifache Punkt (..) ist ein Verweis auf das Elternverzeichnis.
Beispiel 5 (für die Kommandozeile):
Auf die eben beschriebene Verzeichnisstruktur angewendet, liefert der Befehl
die folgende Ausgabe:
Die Parameter /D und /R können auch kombiniert werden.
Beispiel 6 (für die Kommandozeile):
Wenn auf die schon bekannte Verzeichnisstruktur der Befehl
angewendet wird, ist die Ausgabe folgende:
Das Ergebnis ist also gleichwertig zu
Um mit John Cleese zu sprechen: Kommen wir nun zu etwas völlig anderem. Denn diese Variante der FOR-Schleife verhält sich genauso wie in "richtigen" Programmiersprachen. Die Laufvariable ist eine numerische Größe, die beim Start der Schleife mit dem Wert von Start initialisiert und nach jedem Durchlauf um den Wert von Schritt erhöht wird (negative Schrittweiten sind auch möglich). Die Schleife wird nach dem Durchlauf beendet, in dem die Laufvariable den Wert von Ende erreicht hat.
Beispiel 7:
Das folgende Batchfile, auf die schon bekannte Verzeichnisstruktur E:\Archiv\Audio angewendet,
liefert folgende Ausgabe:
Hier wird die verzögerte Variablenerweiterung benutzt, da sich der Inhalt der Variablen %cntr% in der ersten FOR-Schleife bei jedem Durchlauf um 1 erhöhen soll. Je nach Anzahl der gefundenen Dateien werden die Variablen file1 bis fileN erzeugt, die in der zweiten FOR-Schleife mit SET ausgegeben werden.
So, kommen wir jetzt zu der Variante, wegen der sicherlich die meisten überhaupt ein Tutorial für die FOR-Schleife benötigen, denn FOR /F ist eine Eierlegendewollmilchsau. Und wie das so ist mit Werkzeugen, die man für viele Zwecke einsetzen kann, sind sie nicht mal eben so mit links zu benutzen. Hinzu kommt, dass die Auswertung von Ausdruck etwas buggy ist und die Hilfe von FOR zu dieser Variante mehr Verwirrung stiftet als hilfreich zu sein und einige Details verschweigt.
(Hallo Biber!). Wer weiss, wie's geht, möge das bitte posten. [EDIT] pieh-ejdsch hat inzwischen hier veröffentlicht, wie es geht. [/EDIT]
Wenn auf diese Art festgelegt wurde, dass mehrere Tokens (Teilabschnitte) einer Zeichenkette/(Datei-)Zeile verarbeitet werden sollen, entsteht automatisch für jedes Token eine zusätzliche Laufvariable. Deren Namen hängen vom Namen der vom Programmierer festgelegten Laufvariablen ab, sie werden in aufsteigender Reihenfolge nach den im Alphabet folgenden Buchstaben benannt. Zur Verdeutlichung ein Beispiel:
Beispiel 8 (für die Kommandozeile):
Das ist die Untervariante zur Verarbeitung der Ausgabe von Programmen/Befehlen. Zunächst wird der DIR-Befehl in der Klammer ausgeführt, der durch die Option /a:-d keine Unterverzeichnisse listet. So ein Verzeichnislisting sieht z.B. so aus:
Die ersten 5 Zeilen dieser Ausgabe interessieren uns nicht, deshalb SKIP=5.
In den nun folgenden 3 Zeilen mit den Dateien ist das erste Token das Erstellungsdatum, denn nach dem Datum folgen Leerzeichen, die zu den Standardtrennzeichen gehören. Da in der Befehlszeile TOKENS=1,4 steht, wird der Laufvariablen %f (von mir festgelegt) das Dateidatum zugewiesen.
Das zweite Token ist die Uhrzeit der Dateierstellung, das dritte Token die Dateigröße. Diese beiden Tokens werden ignoriert.
Das vierte Token ist der Dateiname mit Erweiterung. Da dieses Token laut Befehlszeile verarbeitet werden soll, wird automatisch eine weitere Laufvariable %g (g folgt im Alphabet auf f) erzeugt und bekommt den Dateinamen zugewiesen.
Außer bei der Datei mit dem Namen "Mein Text.txt". Dieser Name enthält ein Leerzeichen, also ein Trennzeichen. Deshalb enthält %g nur "Mein". Wie kommt man an den Rest des Dateinamens? Der Code muss folgendermaßen geändert werden:
Beispiel 9 (für die Kommandozeile):
Endet die Liste der zu verwendenden Tokens mit einem Stern (*), wird automatisch eine weitere Laufvariable erzeugt, die alle Tokens inkl. Trennzeichen nach dem zuletzt genannten Token (hier dem 4.) enthält. Token 1 steht in %f (von mir festgelegt), Token 4 steht in %g (automatisch erzeugt). Der Rest der Ausgabezeile steht also in %h (auch automatisch erzeugt). Wenn %h nichts enthält, enthält der Dateiname kein Leerzeichen und es genügt, echo %g vom %f auszuführen. Wenn %h jedoch einen Inhalt hat, muss echo %g %h vom %f ausgeführt werden; zwischen %g und %h muss das als Trennzeichen "verschluckte" Leerzeichen von Hand wieder eingefügt werden.
Sinnvoll ist die Ausgabe des Beispielcodes aber dennoch nicht:
Die letzten beiden Zeilen sind Unsinn. Hier tritt eine Schwäche von FOR /F zu Tage: Es gibt zwar die Möglichkeit (mit SKIP=) eine bestimmte Anzahl Zeilen vom Anfang der zu verarbeitenden Datei/Befehlsausgabe zu überspringen, aber keine Möglichkeit, eine bestimmte Anzahl Zeilen am Ende zu ignorieren oder nur eine bestimmte Anzahl Zeilen zu verarbeiten. Für das in diesem Beispiel behandelte Problem existiert aber trotzdem eine Lösung, die ich im Abschnitt Erweiterung von Laufvariablen vorstellen werde.
Wenn man tatsächlich einmal in die Lage kommt, 31 Tokens verarbeiten zu müssen, stellt sich die Frage nach dem Namensraum der dann nötigen Laufvariablen. Meine Versuche haben ergeben, dass man in diesem Fall der selbst benannten Laufvariablen am besten den Namen %? gibt. Es entstehen dann die automatischen Laufvariablen %@, %A, ..., %Z, %[, %\ und %]. Das ergibt einen zusammenhängenden Namensraum von 31 Variablen, die sich ohne Probleme benutzen lassen. Problemkinder im 7Bit-ASCII-Codebereich sind z.B. %,<,> und ^. Ausserdem ist es ungeschickt, die Ziffern 0 bis 9 in den Namensraum aufzunehmen, da man dann die Möglichkeit verliert, auf die Variablen %0 ... %9, die die Parameter des Batchfiles/des Unterprogramms repräsentieren, Bezug zu nehmen. Im erweiterten 8Bit-ASCII-Codebereich jenseits von Zeichen 127 gibt es auch immer wieder einzelne Zeichen oder kleine Blöcke von Zeichen, die sich nicht als Variablennamen verwenden lassen.
oder bei Verwendung der Option USEBACKQ
Die Zeichenkette wird als eine Zeile einer Datei aufgefasst und entsprechend der Optionen DELIMS=, TOKENS= und EOL= zerlegt. Die Zeichenkette kann bei der Version ohne die Option USEBACKQ auch selbst Anführungszeichen enthalten, bei der Version mit USEBACKQ sollten jedoch keine Apostrophe in der Zeichenkette enthalten sein. Bei meinen Tests kam es in diesem Fall bei direkter Eingabe des Befehls an der Kommandozeile nach einem Verzeichniswechsel zu Fehlfunktionen von CMD.EXE. Die umgebenden Anführungszeichen werden nicht an die Laufvariable(n) zugewiesen. Bei Angabe von mehreren Zeichenketten kommt es auch zu Fehlfunktionen von CMD.EXE.
Beispiel 10:
In Batch-Skript kann mit dem Befehl SET /A gerechnet werden. Zahlen, die führende Nullen enthalten, werden dabei als Zahlen im Oktalsystem (zur Basis 8) interpretiert. Das führt beim Verrechnen von 08, 09, 018, 019 usw. zu Fehlermeldungen (diese Zahlen gibt es im Oktalsystem nicht), bei anderen Zahlen zu falschen Ergebnissen. Deshalb hier ein Schnipsel, der diese führenden Nullen abschneidet.
Als Trennzeichen wird durch DELIMS=0 die Ziffer 0 vereinbart. Durch TOKENS=* enthält die Laufvariable alle Tokens inkl. der vereinbarten Trennzeichen. Das erste Token beginnt aber erst dann, wenn ein vom Trennzeichen verschiedenes Zeichen auftritt, also bei der ersten von 0 verschiedenen Ziffer. Deshalb werden die führenden Nullen abgeschnitten, später auftretende Nullen bleiben erhalten. Zeile 10 dient nur dazu, die Variable %Number% auf 0 zu setzen, wenn 0 oder z.B. 0000 eingegeben oder einfach nur ENTER gedrückt wurde.
Ein Manko hat der Code: Man muss Zahlen eingeben, die Eingabe von Buchstaben wird nicht verhindert (Hallo Timo!). Im Abschnitt Praxistipps habe ich einen Schnipsel notiert, der das abfängt. Ihn hier zu verwenden wäre verfrüht, ich würde im Tutorial vorgreifen.
Beispiel 11:
Mit FOR /F und Zeichenketten lassen sich Arrays simulieren. Die Option TOKENS= kann dazu benutzt werden, um den Index des Elements im Array anzugeben, auf das zugegriffen werden soll. Im nachfolgenden Beispiel wird das Standard-Trennzeichen SPACE (Leerzeichen) verwendet, um die "Arrayelemente" zu separieren. Sollen die Arrayelemente selbst Leerzeichen enthalten, kann mit DELIMS=, z.B. das Komma als Trennzeichen vereinbart werden.
In Zeile 13 wird die erste Hex-Ziffer von links (ab dem Offset 0, 1 Zeichen) der Variablen %DIGIT% zugewiesen. In Zeile 14 wird diese Ziffer von der Variablen %HexNum% abgeschnitten (%HexNum% ist gleich Inhalt von %HexNum% ab dem Offset 1 bis zum Ende). Wenn %HexNum% leer ist, wird die Hauptschleife in Zeile 33 beendet.
Die Zeilen 18 bis 28 suchen im Array %HexDigits% nach der aktuell betrachteten Hex-Ziffer %Digit%. Wird sie gefunden, wird am gleichen Index in %ConvArray% das dezimale Äquivalent ausgelesen und die Schleife abgebrochen. Wenn die Schleife bis zum Ende (%Index% gleich 17) durchläuft, bedeutet das, dass ein Zeichen eingegeben wurde, was keine Hex-Ziffer darstellt und das Skript wird mit einer Fehlermeldung abgebrochen.
Man könnte einwenden, die Zeilen 18 bis 28 könnte man doch als FOR /L Schleife programmieren, also den Index damit erzeugen lassen. Hier kommt aber eine Einschränkung von FOR /F zum tragen: Die Werte bei TOKENS=, SKIP=, EOL= und DELIMS= dürfen keine Laufvariable einer anderen FOR-Schleife oder eine verzögert erweiterte Variable (in Ausrufezeichen eingeschlossen) enthalten. Der Grund dafür dürfte in der Art und Weise liegen, wie der Befehlszeileninterpreter eine solche Zeile auswertet und übersetzt.
Übrigens: Der einfachste Hex=>Dec Konverter in Batchskript ist natürlich
oder bei Verwendung der Option USEBACKQ
Die Angabe von mehreren Dateien ist möglich. Die Dateien werden zeilenweise ausgelesen. Jede einzelne Zeile wird entsprechend den Optionen DELIMS=, TOKENS=, EOL= und SKIP= verarbeitet. Die bei der Zerlegung der einzelnen Dateizeilen entstehenden Tokens werden der/den Laufvariablen zugewiesen. Leere Zeilen werden nicht an die Laufvariable(n) zugewiesen.
Bei der Version mit USEBACKQ kann der Dateiname oder -pfad Leerzeichen enthalten, ohne USEBACKQ ist das nicht möglich. Gerade wenn man mit Dateinamen arbeitet, die während des Skriptlaufs dynamisch erzeugt werden, ist es deshalb fast schon Pflicht mit der USEBACKQ-Version zu arbeiten.
Beispiel 12:
Die erste FOR-Schleife in Zeile 11 löscht die Variablen, deren Namen in der Variablen %Vars% angegeben sind.
Die FOR /F Schleife in Zeile 13 liest die Datei, deren Name in der Variablen %ConfigFile% gespeichert ist, ein. Als Trennzeichen zwischen den Tokens wird das Gleichheitszeichen definiert. Die Laufvariable %%v enthält deshalb den Namen der Variablen, die durch TOKENS=1,2 automatisch definierte Laufvariable %%w enthält alles, was rechts vom Gleichheitszeichen in der Konfigurationsdatei steht. Durch das standardmäßig als Zeilenendezeichen definierte Semikolon können Kommentarzeilen in die Konfigurationsdatei geschrieben werden, die mit einem Semikolon beginnen.
Die dritte FOR-Schleife in Zeile 15-20 prüft, ob alle Variablen, deren Namen in der Variablen %Vars% angegeben sind, definiert sind. Wenn eine nicht definierte Variable gefunden wird, wird das Unterprogramm MissingVar mit dem Namen der nicht definierten Variablen als Parameter aufgerufen und eine Fehlermeldung ausgegeben. Außerdem wird die Variable %VarMissed% auf den Wert 1 gesetzt.
Wenn in Zeile 22 festgestellt wird, dass %VarMissed% den Wert 1 hat, wird das Skript abgebrochen.
Wenn die Datei Backup.conf folgenden Aufbau hat,
sind nach Ausführung von Zeile 13 in obigem Skript die Variablen Source, Destination, ExcludeFiles und ExcludeDirs mit den angegebenen Werten belegt.
oder bei Verwendung der Option USEBACKQ
Im Unterschied zur Version ohne USEBACKQ wird der auszuführende Befehl bei der Version mit USEBACKQ in Gravis-Zeichen (SHIFT+Taste neben BACKSPACE, danach SPACE (Leerzeichen) drücken) eingeschlossen.
Befehl1 wird in einer separaten, versteckten Instanz von CMD.EXE ausgeführt. Die Ausgabe erscheint nicht auf dem Bildschirm, sondern wird zeilenweise unter Beachtung der Optionen DELIMS=, TOKENS=, EOL= und SKIP= verarbeitet. Die bei der Zerlegung der einzelnen Ausgabezeilen entstehenden Tokens werden der/den Laufvariablen zugewiesen. Leere Ausgabezeilen werden nicht an die Laufvariable(n) zugewiesen.
Wenn Befehl1 ein Programm ist, dessen Pfad Leerzeichen enthält, muss Befehl1 in Anführungszeichen eingeschlossen werden. Falls Befehl1 zusätzlich einen Parameter benötigt, der Leerzeichen enthält und deshalb ebenfalls in Anführungszeichen eingeschlossen werden muss (z.B. einen Dateipfad), funktioniert FOR /F nur unter Verwendung folgender Syntax:
oder bei Verwendung der Option USEBACKQ
[Edit] Neue Erkenntnisse zum Thema hat pieh-ejdsch in diesem Posting veröffentlicht. [/Edit]
Beispiel 13:
In Zeile 5 wird der Benutzer zur Eingabe einer Zeichenkette aufgefordert, die der Variablen %Str% zugewiesen wird. In Zeile 9 wird diese Zeichenkette zusammen mit Steuerzeichen in die Datei strlen.txt ins Verzeichnis für temporäre Dateien %TEMP% (vordefinierte Umgebungsvariable) geschrieben. Da das Zeichen < zum Sprachumfang von Batchskript gehört (Steuerzeichen für Eingabeumleitung), muss es mit dem Zeichen ^ "escaped" werden. Der Inhalt von strlen.txt sieht nach Eingabe von z.B. Ein String folgendermaßen aus:
Der FINDSTR-Befehl in der Klammer der FOR-Schleife in Zeile 10 durchsucht die Datei strlen.txt wegen dem Parameter /b nach Zeilen, die mit < beginnen und gibt diese aus. Vor dem Zeileninhalt wird wegen dem Parameter /o der Offset des ersten Zeichens der Zeile relativ zum Dateianfang, gefolgt von einem Doppelpunkt, ausgegeben. Die Ausgabe sieht für das Beispiel also so aus:
Diese Ausgabe wird ja bekanntlich nicht angezeigt, sondern durch die FOR-Schleife verarbeitet. Als Trennzeichen zwischen Tokens ist durch DELIMS=: der Doppelpunkt definiert. Weil die Angabe Tokens=X fehlt, wird nur das erste Token jeder Zeile verarbeitet. Da strlen.txt zwei Zeilen enthält, wird die FOR-Schleife zweimal durchlaufen. Beim zweiten Durchlauf hat die Laufvariable %%l also den Wert 13. Dieser Wert kommt folgendermaßen zustande: Das Zeichen <, nach dem FINDSTR sucht, am Anfang der ersten Zeile von strlen.txt hat den Offset 0. Deshalb gibt FINDSTR in seiner Ausgabe 0: vor der ersten Zeile aus. Das Zeichen g des Wortes String hat den Offset 10. Danach kommen in der Datei noch die zwei Zeichen <CARRIAGE RETURN> (Wagenrücklauf, ASCII-Code 13) und <LINEFEED> (Zeilenvorschub, ASCII-Code 10). Diese werden von FINDSTR mitgezählt. Deshalb hat das erste Zeichen < der zweiten Zeile von strlen.txt den Offset 13 relativ zum Dateianfang. Von diesem Offset muss man den Wagenrücklauf, den Zeilenvorschub und das Zeichen < in der ersten Zeile von strlen.txt (also 3) subtrahieren und erhält somit die Länge der Zeichenkette in Zeile 1 von strlen.txt.
Übrigens: Der Suchstring des FINDSTR-Befehls, das Zeichen <, muss nicht "escaped" werden, da er von Anführungszeichen eingeschlossen ist.
Beispiel 14 (für die Kommandozeile):
Betrachten wir ein Verzeichnis, bei dem DIR E:\Test\*.txt die folgende Ausgabe liefert:
Die folgende Tabelle gibt an, wie die Ausgabe zu jedem hinzugefügten Kürzel aussieht.
Achtung! Der Befehl DIR /b E:\Test\*.txt aus den obigen Beispielen gibt nur die Dateinamen ohne Pfad aus. Wenn das aktuell gesetzte Verzeichnis nicht E:\Test ist und auch dort eine Datei mit Namen Versuch.txt vorhanden ist, werden von %~fF, %~dF, %~pF, %~sF, %~aF, %~tF und %~zF Daten zurückgegeben, die sich auf diese Datei beziehen, nicht auf E:\Test\Versuch.txt! Wenn Doku-Test.txt und Versuch.txt im aktuellen Verzeichnis nicht vorhanden sind, wird keine Fehlermeldung erzeugt, die Angaben zu Laufwerksbuchstaben und Pfaden werden einfach vom aktuellen Verzeichnis übernommen. Um obige Operatoren zu verwenden, immer dafür sorgen, dass die Laufvariable vollständige Pfade enthält!
Die o.g. Kürzel/Operatoren können auch kombiniert werden. So liefert z.B. %~dpF bei obigen Beispieldateien
und %~dpnxF ist gleichwertig zu %~fF.
Beispiel 15 (für die Kommandozeile):
Mit der Erweiterung von Laufvariablen und zwei geschachtelten FOR-Schleifen lässt sich das Problem aus Beispiel 9 nun lösen.
Die äußere FOR-Schleife listet die gewünschten Dateien mit Pfaden. Die innere FOR-Schleife wertet eine Zeichenkette aus, die aus dem Erstellungsdatum und der Erstellungszeit der Dateien besteht. Zwischen diesen beiden Angaben wird durch %~tf ein Leerzeichen erzeugt. Das erste Token ist das Erstellungsdatum, was an die Laufvariable %d zugewiesen wird. Der ECHO-Befehl gibt nur den Dateinamen, ohne Laufwerksbuchstaben und Pfad, sowie das Datum aus.
Wie man sieht, kann in der inneren FOR-Schleife die Laufvariable der äußeren FOR-Schleife benutzt werden.
Beispiel 16
FOR-Schleifen brechen ab, sobald man GOTO verwendet, selbst wenn das Ziel innerhalb der Schleife liegt.
Die Ausgabe ist folgende:
Statt in Zeile 7 den Inhalt der Laufvariablen auszugeben, wird nur noch %n ausgegeben. Von dem doppelten Prozentzeichen wird vom Befehlszeileninterpreter automatisch eines entfernt und das ganze nur noch als Text ausgegeben, weil der Befehl schon nicht mehr im Kontext der FOR-Schleife ausgeführt wird und somit die Laufvariable %%n nicht mehr definiert ist.
Beispiel 17
Aber Abbruch ist hier nicht mit einem sofortigen Abbruch zu verwechseln, hier dauert der Abbruch 10,5 Sekunden (System: Athlon 64 X2 3600+, 2x2GHz).
Die Ausgabe ist z.B. folgende:
An den Zeitangaben sieht man, dass die Schleife anscheinend wirklich 1.000.000 mal durchläuft. Die beiden ECHO-Befehle werden trotzdem jeweils nur einmal ausgeführt. Da hier keine verzögerte Variablenerweiterung aktiviert und benutzt wurde, müsste die Variable %time% auch bei beiden Ausgaben den gleichen Wert haben. Da das nicht so ist, sieht man auch daran, dass Zeile 7 ausserhalb des Kontexts der FOR-Schleife ausgeführt wird.
Ich werde die Postings in diesen Abschnitt integrieren. Mit der letzten Sorte von Schnipseln fange ich hier mal an.
1. Der Code
ist kürzer und schneller als
und liefert vollständige Pfade. Übrigens: Ich habe um den DIR-Befehl einen FOR /F Befehl gelegt, damit das Ergebnis, so wie bei der FOR-Variante, in einer Variablen vorliegt.
2. Der Code
liefert ein ähnliches Ergebnis wie
Die FOR /R Variante ist kürzer und schneller und es wird auch das Basisverzeichnis E:\Test gelistet.
3. Auch
und
sind sich im Ergebnis ähnlich. Abgesehen davon, dass die FOR /R Variante kürzer und schneller ist, wird auch das Wurzelverzeichnis gelistet und die Pfade haben keinen abschließenden Backslash (außer beim Wurzelverzeichnis E:\).
4. Beispiel 10 in einer Version, die die Eingabe von Zeichen, bei denen es sich nicht um Zahlen handelt, abfängt.
Gegenüber dem Beispiel 10 ist als relevanter Code nur Zeile 9 hinzugekommen. Deshalb werde ich nur dazu etwas sagen. Der Befehl FINDSTR wird hier dazu verwendet, um zu prüfen, ob %Number% etwas anderes als Zahlen enthält. Zuerst wird %Number% mit dem ECHO-Befehl ausgegeben. Die Ausgabe wird über eine Pipe an FINDSTR weitergeleitet. Der Suchstring von FINDSTR ist ein regulärer Ausdruck. Durch [^0-9] wird eine Klasse von Zeichen definiert. Diese Klasse enthält alle Zeichen ausser den Ziffern 0 bis 9. Enthält %Number% etwas anderes als Zahlen, findet FINDSTR also etwas, auf was sein Suchstring zutrifft. Dadurch wird die Ausgabe von FINDSTR an die Laufvariable %%c zugewiesen und der Befehl im Schleifenkörper des FOR-Befehls ausgeführt, also der Rücksprung zum Label InputNumber. Damit das ganze funktioniert, ist es wichtig, dass kein Leerzeichen zwischen dem abschließenden Prozentzeichen von %Number% und dem Escapezeichen ^ vor dem Pipezeichen | steht, das würde vom ECHO-Befehl sonst auch ausgegeben und der Rücksprung somit immer ausgeführt.
5. In Beispiel 12 wird ein Schnipsel vorgestellt, der eine Konfigurationsdatei einliesst und dementsprechend Skript-Variablen setzt. Nachteil an der Sache ist, das ein Anwender gezwungen wäre, so eine Konfigurationsdatei mit einem Editor zu schreiben, der im DOS-/ASCII-Mode abspeichern kann, sonst gibt es Probleme bei Verzeichnisnamen, die eins der Zeichen ÄÖÜäöüß enthalten, denn die Befehle der Kommandozeile arbeiten immer noch im ASCII-Modus. Es muss also ein ANSI=>ASCII Konverter her, dann kann man Konfigurationsdateien mit Notepad schreiben.
Das folgende Skript demonstriert dabei gleich noch einen Programmiertrick. Oft ist man nämlich gezwungen, die verzögerte Variablenerweiterung zu benutzen, bekommt dann aber Probleme mit Variablen, die zur Laufzeit ein Ausrufezeichen enthalten können (z.B. weil Datei- oder Verzeichnisnamen darin gespeichert werden sollen). Es gibt aber eine Möglichkeit, auf die verzögerte Variablenerweiterung zu verzichten, indem man den Code, der mit einer in der FOR-Schleife veränderten Variablen arbeitet, in ein Unterprogramm auslagert. Netterweise hat die Variable dort, auch wenn ihr Name statt mit Ausrufezeichen ganz normal mit Prozentzeichen eingeschlossen ist, immer ihren aktuellen Wert. Im nachfolgenden Skript wird diese Technik aber nur deshalb verwendet, weil das Hin- und Herschalten der Codepage in der FOR-Schleife nicht funktioniert hat.
Bei den Erklärungen der einzelnen Varianten des FOR-Befehls werden, wie allgemein üblich, optionale Teile in eckigen Klammern eingeschlossen dargestellt.
Inhaltsverzeichnis
Allgemeines zur FOR-Schleife
Die FOR-Schleife gibt es in mehreren Varianten.- Die Urform und ihre Weiterentwicklungen
FOR [/D] [/R [[Laufwerk:]Pfad]] %Variable IN (Satz) DO Befehl [Parameter]- Variante zur Erzeugung von Zahlenfolgen, funktioniert wie eine FOR-Schleife in normalen Programmiersprachen
FOR /L %Variable IN (Start,Schritt,Ende) DO Befehl [Parameter]- Mehrzweck-Variante
FOR /F ["Optionen"] %Variable IN (Ausdruck) DO Befehl [Parameter]Als Variablennamen dürfen nur einzelne Zeichen verwendet werden. Zwar sind sehr viele der druckbaren ASCII-Zeichen als Variablennamen erlaubt, man sollte sich aber auf die Verwendung von Buchstaben (A-Z oder a-z) beschränken.
Bei den Variablennamen wird zwischen Groß- und Kleinschreibung unterschieden. Die Variable %I ist also etwas anderes als %i.
Möchte man mehr als einen Befehl in der FOR-Schleife ausführen lassen, kann man wie üblich die Befehle mit dem Operator & verketten oder (in Batchfiles) die Befehlszeile durch die Verwendung von Klammern auf mehrere Zeilen "verlängern":
01.
FOR %%Variable IN (Satz) DO ( 02.
Befehl1 [Parameter] 03.
Befehl2 [Parameter] 04.
)Exkurs (verzögerte) Variablenerweiterung
Das Problem mit der Variablenerweiterung tritt auch bei anderen Ausdrücken auf, in denen eine Befehlszeile durch die Verwendung von Klammern verlängert wurde, also z.B. auch beim IF-Befehl. Hier ein Beispiel:01.
setlocal 02.
03.
set "users=C:\Users" 04.
05.
set "home=" 06.
set /p "home=Geben Sie den Benutzernamen ein: " 07.
08.
if not "%home%"=="" ( 09.
set "op_dir=%users%\%home%" 10.
dir "%op_dir%" 11.
)Die Zeilen 8 bis 11 werden vom Befehlsinterpreter als eine Zeile betrachtet. Bevor die Befehle ausgeführt werden, werden alle Variablen durch ihren aktuellen Wert ersetzt (erweitert). Da op_dir zu diesem Zeitpunkt nicht definiert ist, wird Zeile 8 als
dir ""Um zum gewünschten Ergebnis zu kommen, muss die verzögerte Variablenerweiterung aktiviert und benutzt werden. Das Beispiel muss dann so lauten:
01.
setlocal enabledelayedexpansion 02.
03.
set "users=C:\Users" 04.
05.
set "home=" 06.
set /p "home=Geben Sie den Benutzernamen ein: " 07.
08.
if not "%home%"=="" ( 09.
set "op_dir=%users%\%home%" 10.
dir "!op_dir!" 11.
)Werte von Parametern der FOR-Schleife durch Variablen angeben
Beim Verschachteln von FOR-Schleifen ist folgendes zu beachten:Die Variante FOR /R hat einen Parameter Basisverzeichnis und die Variante FOR /F hat die Parameter SKIP=x, TOKENS=x, DELIMS=x und EOL=x. Die Werte all diese Parameter können nicht durch eine Laufvariable einer umgebenden FOR-Schleife oder durch eine verzögert erweiterte Variable (in Ausrufezeichen eingeschlossen) angegeben werden. Lediglich die Angabe als normale Umgebungsvariable (in Prozentzeichen eingeschlossen) ist möglich. Verschachtelte FOR-Schleifen werden, wie schon erwähnt, vom Kommandozeileninterpreter als eine Zeile aufgefasst. Wenn solch eine Zeile übersetzt wird, muss der Wert der Variablen, die als Wert einer der o.g. Parameter angegeben ist, schon feststehen und kann sich während der Abarbeitung der Schleifen auch nicht mehr ändern. Diese Voraussetzungen werden nur von normalen Umgebungsvariablen erfüllt.
Die Urform der FOR-Schleife
Den FOR-Befehl gab es schon zu DOS-Zeiten, allerdings nur in dieser Form:FOR %Variable IN (Satz) DO Befehl [Parameter]Beispiel 1 (für die Kommandozeile):
for %i in ("Hallo Welt" "*.txt" Hallo Welt "E:\My Scripts\*.bat") do @echo %i"Hallo Welt" Test.txt MeinText.txt Hallo Welt E:\My Scripts\Backup.bat E:\My Scripts\RDir.batdir /b "E:\My Scripts\*.bat"Beispiel 2:
01.
@echo off & setlocal 02.
03.
cls 04.
05.
set "Dest=E:\Test" 06.
07.
set /p "FileName=Geben Sie einen Dateinamen ein: " 08.
if "%FileName%" equ "" goto :EOF 09.
10.
:SelectAction 11.
set /p "Action=Möchten Sie diese Datei kopieren oder verschieben (A für Abbruch) (K/V/A)? " 12.
for %%i in (K V A) do if /i "%Action%" equ "%%i" goto :%%i 13.
goto :SelectAction 14.
15.
:K 16.
copy "%FileName%" "%Dest%" 17.
goto :EOF 18.
19.
:V 20.
move "%FileName%" "%Dest%" 21.
goto :EOF 22.
23.
:A 24.
endlocalDer Parameter /D
Bei Angabe des Parameters /D wird bei einer Zeichenkette mit Wildcards nur nach Verzeichnissen gesucht, auf deren Name die angegebene Maske passt. Wird die Maske inkl. Pfad angegeben, wird den Namen der gefundenen Verzeichnisse der Pfad vorangestellt und der Laufvariablen zugewiesen.Beispiel 3 (für die Kommandozeile):
for /d %i in ("E:\My *") do @echo %iE:\My Scripts E:\My DocsDer Parameter /R
Bei Angabe des Parameters /R wird die FOR-Schleife ausgehend vom Verzeichnis [Laufwerk:]Pfad rekursiv für jedes darunter liegende Verzeichnis ausgeführt. Wenn kein Basis-Verzeichnis angegeben wird, wird das aktuelle Verzeichnis verwendet. Für [Laufwerk:]Pfad kann keine Laufvariable einer anderen FOR-Schleife oder eine verzögert erweiterte Variable (in Anführungszeichen eingeschlossen) angegeben werden! Satz ist eine Folge von Zeichenketten, die, wenn sie Wildcards (* oder ?) enthalten, als Maske für Dateinamen interpretiert werden. Alle gefundenen Dateien aus den durchsuchten Verzeichnissen werden inkl. Pfad nacheinander an die Laufvariable zugewiesen. Enthält Satz zusätzlich eine Zeichenkette ohne Wildcards, werden die Pfade aller durchsuchten Verzeichnisse, ergänzt um \Zeichenkette, an die Laufvariable zugewiesen. Ist die Zeichenkette in Anführungszeichen eingeschlossen, wird \"Zeichenkette" ergänzt.Beispiel 4 (für die Kommandozeile):
for /r "E:\Archiv" %i in ("Audio\*.mp3" Test1 "Test2") do @echo %iE:\Archiv\Audio\Test1.mp3 E:\Archiv\Audio\Test2.mp3 E:\Archiv\Audio\Track01.mp3 E:\Archiv\Audio\Track02.mp3 E:\Archiv\Test1 E:\Archiv\"Test2" E:\Archiv\Audio\Test1 E:\Archiv\Audio\"Test2"Enthält Satz lediglich einen Punkt, erhält man unter Verwendung der Erweiterung von Laufvariablen einen Ersatz für
dir /s /b /a:d [[Laufwerk:]Pfad]Beispiel 5 (für die Kommandozeile):
Auf die eben beschriebene Verzeichnisstruktur angewendet, liefert der Befehl
for /r "E:\Archiv" %i in (.) do @echo %~fiE:\Archiv E:\Archiv\AudioDie Parameter /D und /R können auch kombiniert werden.
Beispiel 6 (für die Kommandozeile):
Wenn auf die schon bekannte Verzeichnisstruktur der Befehl
for /d /r "E:\Archiv" %i in (*) do @echo %iE:\Archiv\Audiodir /s /b /a:d E:\ArchivDie Variante zur Erzeugung von Zahlenfolgen, FOR /L
FOR /L %Variable IN (Start,Schritt,Ende) DO Befehl [Parameter]Beispiel 7:
Das folgende Batchfile, auf die schon bekannte Verzeichnisstruktur E:\Archiv\Audio angewendet,
01.
@echo off 02.
03.
setlocal enabledelayedexpansion 04.
05.
cls 06.
07.
set "cntr=0" 08.
09.
for %%i in ("E:\Archiv\Audio\*.*") do ( 10.
set /a "cntr+=1" 11.
set "file!cntr!=%%i" 12.
) 13.
14.
for /l %%i in (1,1,%cntr%) do set file%%ifile1=E:\Archiv\Audio\Test1.mp3 file2=E:\Archiv\Audio\Test2.mp3 file3=E:\Archiv\Audio\Track01.mp3 file4=E:\Archiv\Audio\Track02.mp3Die Mehrzweck-Variante, FOR /F
FOR /F ["Optionen"] %Variable IN (Ausdruck) DO Befehl [Parameter]Allgemeines zu FOR /F
FOR /F hat drei Untervarianten. Welche verwendet wird, hängt vom Aufbau von Ausdruck und dem Vorhandensein der Option USEBACKQ ab. Man kann- eine Zeichenkette verarbeiten.
- den Inhalt von mehreren explizit angegebenen Dateien (keine Verwendung von Wildcards) zeilenweise verarbeiten.
- die Ausgabe eines Programms/Befehls zeilenweise verarbeiten.
Trennzeichen (Option DELIMS=)
Allen drei Untervarianten ist gemeinsam, dass sie die zu verarbeitenden Zeichenketten/(Datei-)Zeilen in sog. Tokens zerlegen. Ein Token ist ein Teilabschnitt des Gesamtstrings. Wo ein Token beginnt und endet wird durch Trennzeichen (Delimiter) bestimmt. Die Standard-Trennzeichen sind Leerzeichen und Tabzeichen. Man kann aber auch über die Option DELIMS= eigene Trennzeichen definieren oder die Verwendung von Trennzeichen komplett abschalten. Das Anführungszeichen " als Trennzeichen zu definieren ist nicht möglich. Gerüchteweise soll es unter fluchen möglich seinZeilenendezeichen (Option EOL=)
Der Zeilenumbruch zählt natürlich auch als Trennzeichen zwischen Tokens und markiert das Zeilenende. Man kann aber über die Option EOL= auch ein eigenes Zeilenendezeichen definieren (EOL=End Of Line). Sobald ein solches Zeichen von FOR /F eingelesen wird, wird die Verarbeitung einer Zeichenkette/(Datei-)Zeile beendet. Wenn man ein Skript schreiben will, das Konfigurationsdateien auswertet, kann man über EOL=: z.B. den Doppelpunkt als Kommentarzeichen definieren. Zeilen, die damit beginnen, werden dann nicht ausgewertet. Standardmäßig ist das Semikolon (;) das EOL-Zeichen.Zeilen überspringen (Option SKIP=)
Mit der Option SKIP= kann eine Anzahl Zeilen angegeben werden, die vom Anfang einer Datei/Befehlsausgabe übersprungen werden sollen. Diese Zeilen werden nicht verarbeitet. Die Angabe von SKIP=0 ist nicht möglich.Tokens (Option TOKENS=)
Die durch die Zerlegung entstehenden Tokens werden der Laufvariablen zugewiesen. Standardmäßig wird immer nur das erste erhaltene Token zugewiesen. Dieses Verhalten kann jedoch über die Option TOKENS= verändert werden. Die Option erwartet eine durch Komma getrennte Aufzählung der zu verwendenden Token-Nummern, z.B. TOKENS=1,3,4,5. Die Angabe von Bereichen ist auch möglich, z.B. TOKENS=1,3-5. Die Angabe von TOKENS=0 ist nicht möglich. Die höchst mögliche Tokennummer ist 31.Wenn auf diese Art festgelegt wurde, dass mehrere Tokens (Teilabschnitte) einer Zeichenkette/(Datei-)Zeile verarbeitet werden sollen, entsteht automatisch für jedes Token eine zusätzliche Laufvariable. Deren Namen hängen vom Namen der vom Programmierer festgelegten Laufvariablen ab, sie werden in aufsteigender Reihenfolge nach den im Alphabet folgenden Buchstaben benannt. Zur Verdeutlichung ein Beispiel:
Beispiel 8 (für die Kommandozeile):
for /f "skip=5 tokens=1,4" %f in ('dir /a:-d E:\Test') do @echo %g vom %f Datenträger in Laufwerk E: ist DATA Volumeseriennummer: 1212-3434 Verzeichnis von E:\Test 08.10.2010 13:16 1.620 Test.txt 05.08.2010 15:38 315 Mein Text.txt 30.10.2009 18:52 8.519 Anleitung.txt 3 Datei(en) 10.454 Bytes 0 Verzeichnis(se), 36.387.303.424 Bytes frei In den nun folgenden 3 Zeilen mit den Dateien ist das erste Token das Erstellungsdatum, denn nach dem Datum folgen Leerzeichen, die zu den Standardtrennzeichen gehören. Da in der Befehlszeile TOKENS=1,4 steht, wird der Laufvariablen %f (von mir festgelegt) das Dateidatum zugewiesen.
Das zweite Token ist die Uhrzeit der Dateierstellung, das dritte Token die Dateigröße. Diese beiden Tokens werden ignoriert.
Das vierte Token ist der Dateiname mit Erweiterung. Da dieses Token laut Befehlszeile verarbeitet werden soll, wird automatisch eine weitere Laufvariable %g (g folgt im Alphabet auf f) erzeugt und bekommt den Dateinamen zugewiesen.
Außer bei der Datei mit dem Namen "Mein Text.txt". Dieser Name enthält ein Leerzeichen, also ein Trennzeichen. Deshalb enthält %g nur "Mein". Wie kommt man an den Rest des Dateinamens? Der Code muss folgendermaßen geändert werden:
Beispiel 9 (für die Kommandozeile):
for /f "skip=5 tokens=1,4,*" %f in ('dir E:\Test /a:-d') do @(if "%h" equ "" (echo %g vom %f) else (echo %g %h vom %f))Sinnvoll ist die Ausgabe des Beispielcodes aber dennoch nicht:
Test.txt vom 08.10.2010 Mein Text.txt vom 05.08.2010 Anleitung.txt vom 30.10.2009 Bytes vom 3 Bytes frei vom 0Wenn man tatsächlich einmal in die Lage kommt, 31 Tokens verarbeiten zu müssen, stellt sich die Frage nach dem Namensraum der dann nötigen Laufvariablen. Meine Versuche haben ergeben, dass man in diesem Fall der selbst benannten Laufvariablen am besten den Namen %? gibt. Es entstehen dann die automatischen Laufvariablen %@, %A, ..., %Z, %[, %\ und %]. Das ergibt einen zusammenhängenden Namensraum von 31 Variablen, die sich ohne Probleme benutzen lassen. Problemkinder im 7Bit-ASCII-Codebereich sind z.B. %,<,> und ^. Ausserdem ist es ungeschickt, die Ziffern 0 bis 9 in den Namensraum aufzunehmen, da man dann die Möglichkeit verliert, auf die Variablen %0 ... %9, die die Parameter des Batchfiles/des Unterprogramms repräsentieren, Bezug zu nehmen. Im erweiterten 8Bit-ASCII-Codebereich jenseits von Zeichen 127 gibt es auch immer wieder einzelne Zeichen oder kleine Blöcke von Zeichen, die sich nicht als Variablennamen verwenden lassen.
Die Auswertung von Ausdruck beeinflussen (Option USEBACKQ)
Die Option USEBACKQ kann nur im Kontext der verschiedenen Untervarianten von FOR /F erklärt werden. Ich verweise deshalb auf die entsprechenden Abschnitte.Verarbeiten einer Zeichenkette
Die Syntax zum Verarbeiten einer Zeichenkette ist:FOR /F ["Optionen"] %Variable IN ("Zeichenkette") DO Befehl [Parameter]FOR /F "usebackq[ Optionen]" %Variable IN ('Zeichenkette') DO Befehl [Parameter]Beispiel 10:
In Batch-Skript kann mit dem Befehl SET /A gerechnet werden. Zahlen, die führende Nullen enthalten, werden dabei als Zahlen im Oktalsystem (zur Basis 8) interpretiert. Das führt beim Verrechnen von 08, 09, 018, 019 usw. zu Fehlermeldungen (diese Zahlen gibt es im Oktalsystem nicht), bei anderen Zahlen zu falschen Ergebnissen. Deshalb hier ein Schnipsel, der diese führenden Nullen abschneidet.
01.
::Entfernen von führenden Nullen bei Zahlen, damit sie 02.
::in Berechnungen nicht als Oktalzahlen aufgefasst werden 03.
04.
@echo off & setlocal 05.
06.
cls 07.
set /p "Number=Geben Sie eine Zahl mit oder ohne führende Nullen ein: " 08.
09.
for /f "tokens=* delims=0" %%n in ("%Number%") do set "Number=%%n" 10.
set /a "Number=Number" 11.
12.
echo Die eingegebene Zahl ist %Number%Ein Manko hat der Code: Man muss Zahlen eingeben, die Eingabe von Buchstaben wird nicht verhindert (Hallo Timo!). Im Abschnitt Praxistipps habe ich einen Schnipsel notiert, der das abfängt. Ihn hier zu verwenden wäre verfrüht, ich würde im Tutorial vorgreifen.
Beispiel 11:
Mit FOR /F und Zeichenketten lassen sich Arrays simulieren. Die Option TOKENS= kann dazu benutzt werden, um den Index des Elements im Array anzugeben, auf das zugegriffen werden soll. Im nachfolgenden Beispiel wird das Standard-Trennzeichen SPACE (Leerzeichen) verwendet, um die "Arrayelemente" zu separieren. Sollen die Arrayelemente selbst Leerzeichen enthalten, kann mit DELIMS=, z.B. das Komma als Trennzeichen vereinbart werden.
01.
::Hex=>Dec Konverter mit Array-Simulation 02.
03.
@echo off & setlocal 04.
05.
set "HexDigits=0 1 2 3 4 5 6 7 8 9 A B C D E F" 06.
set "ConvArray=0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15" 07.
set "Result=0" 08.
09.
cls 10.
set /p "HexNum=Geben Sie eine hexadezimale Zahl ein (max. 8-stellig): " 11.
12.
:ConvLoop 13.
set "Digit=%HexNum:~0,1%" 14.
set "HexNum=%HexNum:~1%" 15.
16.
set "Index=1" 17.
18.
:ArrayLookup 19.
for /f "tokens=%Index%" %%i in ("%HexDigits%") do ( 20.
if /i "%Digit%" equ "%%i" ( 21.
for /f "tokens=%Index%" %%j in ("%ConvArray%") do ( 22.
set "DecValue=%%j" 23.
goto :CalcResult 24.
) 25.
) 26.
) 27.
28.
set /a "Index+=1" 29.
if %Index% leq 16 (goto :ArrayLookup) else (goto :ForbiddenChar) 30.
31.
:CalcResult 32.
set /a "Result=Result*16+DecValue" 33.
if "%HexNum%" neq "" goto :ConvLoop 34.
35.
echo Das dezimale Äquivalent dieser Zahl ist %Result% 36.
exit /b 37.
38.
:ForbiddenChar 39.
echo Es sind nur die Zeichen 0-9, a-f und A-F zur Eingabe erlaubt. 40.
exit /bDie Zeilen 18 bis 28 suchen im Array %HexDigits% nach der aktuell betrachteten Hex-Ziffer %Digit%. Wird sie gefunden, wird am gleichen Index in %ConvArray% das dezimale Äquivalent ausgelesen und die Schleife abgebrochen. Wenn die Schleife bis zum Ende (%Index% gleich 17) durchläuft, bedeutet das, dass ein Zeichen eingegeben wurde, was keine Hex-Ziffer darstellt und das Skript wird mit einer Fehlermeldung abgebrochen.
Man könnte einwenden, die Zeilen 18 bis 28 könnte man doch als FOR /L Schleife programmieren, also den Index damit erzeugen lassen. Hier kommt aber eine Einschränkung von FOR /F zum tragen: Die Werte bei TOKENS=, SKIP=, EOL= und DELIMS= dürfen keine Laufvariable einer anderen FOR-Schleife oder eine verzögert erweiterte Variable (in Ausrufezeichen eingeschlossen) enthalten. Der Grund dafür dürfte in der Art und Weise liegen, wie der Befehlszeileninterpreter eine solche Zeile auswertet und übersetzt.
Übrigens: Der einfachste Hex=>Dec Konverter in Batchskript ist natürlich
set /a "DecNum=0x%HexNum%"Verarbeiten des Inhalts von Dateien
Die Syntax zum Verarbeiten des Inhalts von Dateien ist:FOR /F ["Optionen"] %Variable IN (Dateiname[ ...]]) DO Befehl [Parameter]FOR /F "usebackq[ Optionen]" %Variable IN ("Dateiname"[ ...]]) DO Befehl [Parameter]Bei der Version mit USEBACKQ kann der Dateiname oder -pfad Leerzeichen enthalten, ohne USEBACKQ ist das nicht möglich. Gerade wenn man mit Dateinamen arbeitet, die während des Skriptlaufs dynamisch erzeugt werden, ist es deshalb fast schon Pflicht mit der USEBACKQ-Version zu arbeiten.
Beispiel 12:
01.
::Konfigurationsdatei einlesen 02.
::und dadurch Skript-Variablen setzen 03.
04.
@echo off & setlocal 05.
06.
set "ConfigFile=C:\Programme\Backup\Backup.conf" 07.
08.
set "VarMissed=0" 09.
set "Vars=Source Destination ExcludeFiles ExcludeDirs" 10.
11.
for %%v in (%Vars%) do set "%%v=" 12.
13.
for /f "usebackq tokens=1,2 delims==" %%v in ("%ConfigFile%") do set "%%v=%%w" 14.
15.
for %%v in (%Vars%) do ( 16.
if not defined %%v ( 17.
call :MissingVar %%v 18.
set "VarMissed=1" 19.
) 20.
) 21.
22.
if %VarMissed% equ 1 goto :ScriptEnd 23.
. 24.
. 25.
. 26.
:ScriptEnd 27.
endlocal 28.
exit /b 29.
30.
31.
:MissingVar 32.
echo Variable %1 ist in der Konfigurationsdatei nicht definiert. 33.
exit /bDie FOR /F Schleife in Zeile 13 liest die Datei, deren Name in der Variablen %ConfigFile% gespeichert ist, ein. Als Trennzeichen zwischen den Tokens wird das Gleichheitszeichen definiert. Die Laufvariable %%v enthält deshalb den Namen der Variablen, die durch TOKENS=1,2 automatisch definierte Laufvariable %%w enthält alles, was rechts vom Gleichheitszeichen in der Konfigurationsdatei steht. Durch das standardmäßig als Zeilenendezeichen definierte Semikolon können Kommentarzeilen in die Konfigurationsdatei geschrieben werden, die mit einem Semikolon beginnen.
Die dritte FOR-Schleife in Zeile 15-20 prüft, ob alle Variablen, deren Namen in der Variablen %Vars% angegeben sind, definiert sind. Wenn eine nicht definierte Variable gefunden wird, wird das Unterprogramm MissingVar mit dem Namen der nicht definierten Variablen als Parameter aufgerufen und eine Fehlermeldung ausgegeben. Außerdem wird die Variable %VarMissed% auf den Wert 1 gesetzt.
Wenn in Zeile 22 festgestellt wird, dass %VarMissed% den Wert 1 hat, wird das Skript abgebrochen.
Wenn die Datei Backup.conf folgenden Aufbau hat,
;Datenquelle Source=D:\Daten ;Backup-Ziel Destination=P:\Backup ;Dateitypen, die nicht kopiert werden sollen ExcludeFiles=*.bak,*.tmp ;Verzeichnisse, die nicht kopiert werden sollen ExcludeDirs=\Test,\Skript\VersuchVerarbeiten der Ausgabe eines Programms/Befehls
Die Syntax zum Verarbeiten der Ausgabe eines Programms/Befehls ist:FOR /F ["Optionen"] %Variable IN ('Befehl1 [Parameter]') DO Befehl2 [Parameter]FOR /F "usebackq[ Optionen]" %Variable IN (`Befehl1 [Parameter]`) DO Befehl2 [Parameter]Befehl1 wird in einer separaten, versteckten Instanz von CMD.EXE ausgeführt. Die Ausgabe erscheint nicht auf dem Bildschirm, sondern wird zeilenweise unter Beachtung der Optionen DELIMS=, TOKENS=, EOL= und SKIP= verarbeitet. Die bei der Zerlegung der einzelnen Ausgabezeilen entstehenden Tokens werden der/den Laufvariablen zugewiesen. Leere Ausgabezeilen werden nicht an die Laufvariable(n) zugewiesen.
Wenn Befehl1 ein Programm ist, dessen Pfad Leerzeichen enthält, muss Befehl1 in Anführungszeichen eingeschlossen werden. Falls Befehl1 zusätzlich einen Parameter benötigt, der Leerzeichen enthält und deshalb ebenfalls in Anführungszeichen eingeschlossen werden muss (z.B. einen Dateipfad), funktioniert FOR /F nur unter Verwendung folgender Syntax:
FOR /F ["Optionen"] %Variable IN ('call "Befehl1" "Parameter"') DO Befehl2 [Parameter]FOR /F "usebackq[ Optionen]" %Variable IN (`call "Befehl1" "Parameter"`) DO Befehl2 [Parameter]Escapen von Steuerzeichen
Wenn Befehl1 bestimmte Zeichen/Zeichenkombinationen enthält, müssen diese unter Verwendung des Zeichens ^ "escaped" werden, d.h. das Escapezeichen ^ muss ihnen vorangestellt werden um zu verdeutlichen, dass das nachfolgende Zeichen nicht als Steuerzeichen interpretiert, sondern genau so an die Befehl1 ausführende Instanz von CMD.EXE übergeben werden soll. Die folgende Tabelle gibt einen Überblick.[Edit] Neue Erkenntnisse zum Thema hat pieh-ejdsch in diesem Posting veröffentlicht. [/Edit]
| Zeichen | Bedeutung | "escapen" mit |
|---|---|---|
| > | Ausgabeumleitung (in Datei) | ^> |
| < | Eingabeumleitung (aus Datei) | ^< |
| 2>&1 | Standardfehlerkanal (Kanal 2) dorthin umleiten, wohin auch die Standardausgabe (Kanal 1 ) umgeleitet ist | 2^>^&1 |
| | | Ausgabeumleitung eines Befehls in eine Pipe | ^| |
| & | Befehlsverkettung | ^& |
| && | Bedingte Befehlsverkettung Bei Erfolg des vorhergehenden Befehls | ^&^& |
| | | | Bedingte Befehlsverkettung Bei Misserfolg des vorhergehenden Befehls | ^|^| |
| ( | öffnende Klammer | ^( |
| ) | schließende Klammer | ^) |
| = | Zuweisungen | ^= |
| ^ | das Escapezeichen selbst | ^^ |
Beispiel 13:
01.
::Bestimmen der Länge einer Zeichenkette 02.
03.
@echo off & setlocal 04.
05.
set /p "Str=Geben Sie einen String ein: " 06.
07.
set "StrLen=0" 08.
09.
>"%TEMP%\strlen.txt" (echo ^<%Str%&echo ^<) 10.
for /f "delims=:" %%l in ('findstr /o /b "<" "%TEMP%\strlen.txt"') do set /a "StrLen=%%l-3" 11.
12.
del "%TEMP%\strlen.txt" 13.
14.
echo Die Länge von %Str% ist %StrLen%<Ein String <0:<Ein String 13:<Übrigens: Der Suchstring des FINDSTR-Befehls, das Zeichen <, muss nicht "escaped" werden, da er von Anführungszeichen eingeschlossen ist.
Erweiterung von Laufvariablen
Der Inhalt von Laufvariablen der FOR-Schleife kann durch hinzufügen bestimmter Kürzel zum Variablennamen verändert dargestellt werden. Fast alle Funktionen beziehen sich auf den Fall, dass die Laufvariable einen Dateinamen/-pfad enthält. In der folgenden Tabelle wird angenommen, dass die Laufvariable %I ist. Die gleichen Möglichkeiten zur Manipulation hat man auch bei den Variablen %0, %1, ... , %9, die die einem Batchfile/Unterprogramm übergebenen Parameter repräsentieren. Die Beschreibung habe ich weitgehend aus der FOR-Hilfe übernommen.| %~I | Erweitert %I und entfernt alle umschließenden Anführungszeichen ("). |
| %~fI | Erweitert %I zu einem vollständigen Dateinamen inkl. Laufwerksbuchstaben und Pfad. |
| %~dI | Erzeugt nur den Laufwerksbuchstaben von %I. |
| %~pI | Erzeugt nur den Pfad von %I ohne Laufwerksbuchstaben. |
| %~nI | Erzeugt nur den Dateinamen von %I. |
| %~xI | Erzeugt nur die Dateierweiterung von %I. |
| %~sI | Erweitert %I zu einem vollständigen Dateinamen, in dem nur kurze Datei-/Verzeichnisnamen im 8.3-Format vorkommen. |
| %~aI | Erzeugt die Dateiattribute von %I. |
| %~tI | Erzeugt Datum und Zeit von %I. |
| %~zI | Erzeugt die Dateigröße von %I. |
| %~$PATH:I | Durchsucht die in der PATH-Umgebungsvariablen angegebenen Verzeichnisse nach der Datei, deren Name in %I steht und erweitert den Ausdruck auf Laufwerksbuchstabe:\Pfad\Dateiname.Erweiterung der ersten gefundenen Datei. Statt PATH kann auch eine andere Umgebungsvariable angegeben werden. Wenn der Name der Umgebungsvariablen nicht definiert ist oder die Datei, deren Name in %I steht, bei der Suche nicht gefunden wurde, wird der Ausdruck zu einer leeren Zeichenkette erweitert. |
Beispiel 14 (für die Kommandozeile):
Betrachten wir ein Verzeichnis, bei dem DIR E:\Test\*.txt die folgende Ausgabe liefert:
Datenträger in Laufwerk E: ist DATA Volumeseriennummer: 1212-3434 Verzeichnis von E:\Test 30.10.2010 18:52 8.519 Doku-Test.txt 09.09.2009 22:21 298 Versuch.txt 2 Datei(en) 8.817 Bytes 0 Verzeichnis(se), 36.579.278.848 Bytes freiDie folgende Tabelle gibt an, wie die Ausgabe zu jedem hinzugefügten Kürzel aussieht.
| Befehl | Ausgabe |
|---|---|
| for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~fF | E:\Test\Doku-Test.txt E:\Test\Versuch.txt |
| for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~dF | E: E: |
| for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~pF | \Test\ \Test\ |
| for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~nF | Doku-Test Versuch |
| for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~xF | .txt .txt |
| for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~sF | E:\Test\Doku-T~1.txt E:\Test\Versuch.txt |
| for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~aF | --a------ --a------ |
| for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~tF | 30.10.2010 18:52 09.09.2009 22:21 |
| for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~zF | 8519 298 |
| for %F in ("cmd.exe") do @echo %~F | cmd.exe |
| for %F in ("cmd.exe") do @echo %~$Path:F | C:\WINDOWS\system32\cmd.exe |
Achtung! Der Befehl DIR /b E:\Test\*.txt aus den obigen Beispielen gibt nur die Dateinamen ohne Pfad aus. Wenn das aktuell gesetzte Verzeichnis nicht E:\Test ist und auch dort eine Datei mit Namen Versuch.txt vorhanden ist, werden von %~fF, %~dF, %~pF, %~sF, %~aF, %~tF und %~zF Daten zurückgegeben, die sich auf diese Datei beziehen, nicht auf E:\Test\Versuch.txt! Wenn Doku-Test.txt und Versuch.txt im aktuellen Verzeichnis nicht vorhanden sind, wird keine Fehlermeldung erzeugt, die Angaben zu Laufwerksbuchstaben und Pfaden werden einfach vom aktuellen Verzeichnis übernommen. Um obige Operatoren zu verwenden, immer dafür sorgen, dass die Laufvariable vollständige Pfade enthält!
Die o.g. Kürzel/Operatoren können auch kombiniert werden. So liefert z.B. %~dpF bei obigen Beispieldateien
E:\Test\ E:\Test\Beispiel 15 (für die Kommandozeile):
Mit der Erweiterung von Laufvariablen und zwei geschachtelten FOR-Schleifen lässt sich das Problem aus Beispiel 9 nun lösen.
for %f in ("E:\Test\*.txt") do @(for /f %d in ("%~tf") do @echo %~nf vom %d)Wie man sieht, kann in der inneren FOR-Schleife die Laufvariable der äußeren FOR-Schleife benutzt werden.
FOR-Schleifen und GOTO
Ein GOTO-Befehl innerhalb von FOR-Schleifen, dessen Sprungziel ein Label ist, das ebenfalls innerhalb der FOR-Schleife liegt, führt zwar zu keiner Fehlermeldung, funktioniert aber nicht wie gewünscht. Ich übernehme hier Beispiele von jeb-the-batcher aus seinem Kommentar zu diesem Tutorial.Beispiel 16
FOR-Schleifen brechen ab, sobald man GOTO verwendet, selbst wenn das Ziel innerhalb der Schleife liegt.
01.
@echo off 02.
03.
for /l %%n in (1,1,100) do ( 04.
echo %%n 05.
goto :label 06.
:label 07.
echo %%n 08.
) 09.
10.
echo ende1 %n endeBeispiel 17
Aber Abbruch ist hier nicht mit einem sofortigen Abbruch zu verwechseln, hier dauert der Abbruch 10,5 Sekunden (System: Athlon 64 X2 3600+, 2x2GHz).
01.
@echo off 02.
03.
for /l %%n in (1,1,1000000) do ( 04.
echo %%n - %time% 05.
goto :xx 06.
:xx 07.
echo %%n - %time% 08.
) 09.
10.
echo ende1 - 16:48:54,93 %n - 16:49:05,43 endePraxistipps
Dieser Abschnitt ist als "Mitmach-Abschnitt" gedacht. Wer will, kann einen Schnipsel mit Erklärung posten, der- erstaunlich kurz und trotzdem nützlich ist,
- besonders raffiniert programmiert ist oder
- einfach nur ein besonderes Detail von FOR-Schleifen vorstellt.
Ich werde die Postings in diesen Abschnitt integrieren. Mit der letzten Sorte von Schnipseln fange ich hier mal an.
1. Der Code
for %f in (E:\Test\*.*) do @echo %ffor /f "delims=" %f in ('dir /b /a:-d E:\Test') do @echo %f2. Der Code
for /r E:\Test %d in (.) do @echo %~fdfor /f "delims=" %d in ('dir /s /b /a:d E:\Test') do @echo %d3. Auch
for /r E:\Test %d in (..) do @echo %~fdfor /f "delims=" %d in ('dir /s /b /a:d E:\Test') do @echo %~dpd4. Beispiel 10 in einer Version, die die Eingabe von Zeichen, bei denen es sich nicht um Zahlen handelt, abfängt.
01.
::Entfernen von führenden Nullen bei Zahlen, damit sie 02.
::in Berechnungen nicht als Oktalzahlen aufgefasst werden 03.
04.
@echo off & setlocal 05.
06.
:InputNumber 07.
cls 08.
set /p "Number=Geben Sie eine Zahl mit oder ohne führende Nullen ein: " 09.
for /f %%c in ('echo %Number%^|findstr "[^0-9]"') do goto :InputNumber 10.
11.
for /f "tokens=* delims=0" %%n in ("%Number%") do set "Number=%%n" 12.
set /a "Number=Number" 13.
14.
echo Die eingegebene Zahl ist %Number%5. In Beispiel 12 wird ein Schnipsel vorgestellt, der eine Konfigurationsdatei einliesst und dementsprechend Skript-Variablen setzt. Nachteil an der Sache ist, das ein Anwender gezwungen wäre, so eine Konfigurationsdatei mit einem Editor zu schreiben, der im DOS-/ASCII-Mode abspeichern kann, sonst gibt es Probleme bei Verzeichnisnamen, die eins der Zeichen ÄÖÜäöüß enthalten, denn die Befehle der Kommandozeile arbeiten immer noch im ASCII-Modus. Es muss also ein ANSI=>ASCII Konverter her, dann kann man Konfigurationsdateien mit Notepad schreiben.
Das folgende Skript demonstriert dabei gleich noch einen Programmiertrick. Oft ist man nämlich gezwungen, die verzögerte Variablenerweiterung zu benutzen, bekommt dann aber Probleme mit Variablen, die zur Laufzeit ein Ausrufezeichen enthalten können (z.B. weil Datei- oder Verzeichnisnamen darin gespeichert werden sollen). Es gibt aber eine Möglichkeit, auf die verzögerte Variablenerweiterung zu verzichten, indem man den Code, der mit einer in der FOR-Schleife veränderten Variablen arbeitet, in ein Unterprogramm auslagert. Netterweise hat die Variable dort, auch wenn ihr Name statt mit Ausrufezeichen ganz normal mit Prozentzeichen eingeschlossen ist, immer ihren aktuellen Wert. Im nachfolgenden Skript wird diese Technik aber nur deshalb verwendet, weil das Hin- und Herschalten der Codepage in der FOR-Schleife nicht funktioniert hat.
01.
::Konfigurationsdatei vom ANSI- ins ASCII-Format konvertieren 02.
03.
set "ConfigFile=C:\Programme\Backup\Backup.conf" 04.
set "TempConfigFile=%Temp%\Backup.conf" 05.
06.
::Evtl. vorhandene temporäre Konfigurationsdatei löschen 07.
type NUL > "%TempConfigFile%" 08.
09.
::ANSI-Codepage aktivieren 10.
chcp 1252 > NUL 11.
12.
::Datei zeilenweise konvertieren 13.
for /f "usebackq delims=" %%l in ("%ConfigFile%") do ( 14.
set "line=%%l" 15.
call :Convert2ASCII 16.
) 17.
18.
::Konfiguration einlesen 19.
for /f "usebackq tokens=1,2 delims==" %%v in ("%TempConfigFile%") do set "%%v=%%w" 20.
21.
::Temporäre Konfigurationsdatei löschen 22.
del "%TempConfigFile%" 23.
24.
. 25.
. 26.
. 27.
28.
exit /b 29.
30.
31.
:Convert2ASCII 32.
::auf DOS-Codepage umschalten 33.
chcp 850 > NUL 34.
35.
::Eine Zeile schreiben 36.
>>"%TempConfigFile%" echo %line% 37.
exit /b
Florian.Sauber schreibt am 25.11.2010 um 17:09:55 Uhr
Vorbildlich, Friemler! Daumen hoch!
LG Florian
LG Florian
switcher94 schreibt am 25.11.2010 um 21:19:51 Uhr
der Kandidat hat 100 Punkte
schön geschrieben
Grüsse
Switcher
schön geschrieben
Grüsse
Switcher
jeb-the-batcher schreibt am 25.11.2010 um 22:14:29 Uhr
Hallo Friemler,
recht langer und ausführlicher Beitrag, sehr gut gelungen.
Mir fehlt nur der Abschnitt: "FOR extrem" mit den Grenzen und Erklärungen die richtig in die Tiefe gehen.
Z.B. das man nur mit DisabledDelayedExpansion den Inhalt eines FOR-Parameters sicher verwenden kann (alle Kombinationen von Spezial & Escapezeichen) mit EnabledDelayedExpansion ist das nicht möglich, denn bereits ein "hallo!" scheitert,
extrem wäre ein &^!"&!^"""&!^"""&^!, das ist ohne DelayedExp praktisch nicht zu beherrschen.
Um dann var aber sicher (unabhängig vom Inhalt) benutzen zu können, muss man auf EnabledDelayedExpansion umschalten.
Mit call echo %var% bekommt man sonst ganz schnell Probleme
Oder was geht so? Oder wie und wann wirkt die Expansion im FOR Befehl?
Hierbei scheitert die Expansion von !eins! !zwei! und !drei!, die von !vier! klappt und hat zudem den Vorteil,
dass die Spezialzeichen nicht escaped werden müssen.
Das eins,zwei,drei scheitern liegt daran, dass der FOR-Befehl anders als andere Befehle für die eigenen Parametern generell keine DelayedExpansion durchführt,
dass scheint daran zu liegen, dass der Befehl bereits sehr früh vom Parser erkannt werden muss, um die ganze Klammerlogik aufrecht erhalten zu können (wie beim IF Befehl).
FOR-Loops brechen ab, sobald man ein goto verwendet, selbst wenn das Ziel innerhalb der Loop liegt.
Aber Abbruch ist hier nicht mit einem sofortigem Abbruch zu verwechseln, hier dauert der Abbruch schlappe 16 Sekunden!
Und dann gibt es da noch ... und ... (eben das was in "FOR extrem" stehen sollte)
Grüße
jeb
recht langer und ausführlicher Beitrag, sehr gut gelungen.
Mir fehlt nur der Abschnitt: "FOR extrem" mit den Grenzen und Erklärungen die richtig in die Tiefe gehen.
Z.B. das man nur mit DisabledDelayedExpansion den Inhalt eines FOR-Parameters sicher verwenden kann (alle Kombinationen von Spezial & Escapezeichen) mit EnabledDelayedExpansion ist das nicht möglich, denn bereits ein "hallo!" scheitert,
extrem wäre ein &^!"&!^"""&!^"""&^!, das ist ohne DelayedExp praktisch nicht zu beherrschen.
01.
setlocal DisableDelayedExpansion 02.
FOR /F "delims=*" %%a in ("hallo ^caret!") do ( 03.
echo %%a 04.
set var=%%a 05.
)Um dann var aber sicher (unabhängig vom Inhalt) benutzen zu können, muss man auf EnabledDelayedExpansion umschalten.
Mit call echo %var% bekommt man sonst ganz schnell Probleme
01.
setlocal DisableDelayedExpansion 02.
FOR /F "delims=*" %%a in ("hallo ^caret!") do ( 03.
set var=%%a 04.
setlocal EnableDelayedExpansion 05.
set "var=!var:h=b!" 06.
echo !var! 07.
endlocal 08.
)Oder was geht so? Oder wie und wann wirkt die Expansion im FOR Befehl?
01.
setlocal EnableDelayedExpansion 02.
set eins=1 03.
set zwei=2 04.
set drei=a 05.
set "vier=set /p .=hallo < nul" 06.
FOR /F "tokens=!eins! delims=!zwei!" %%!drei! in ('!vier!') do echo %%adass die Spezialzeichen nicht escaped werden müssen.
Das eins,zwei,drei scheitern liegt daran, dass der FOR-Befehl anders als andere Befehle für die eigenen Parametern generell keine DelayedExpansion durchführt,
dass scheint daran zu liegen, dass der Befehl bereits sehr früh vom Parser erkannt werden muss, um die ganze Klammerlogik aufrecht erhalten zu können (wie beim IF Befehl).
FOR-Loops brechen ab, sobald man ein goto verwendet, selbst wenn das Ziel innerhalb der Loop liegt.
01.
for /L %%n in (1,1,100) DO ( 02.
echo %%n 03.
goto :label 04.
:label 05.
echo %%n 06.
) 07.
echo ende 08.
------------ AUSGABE ------ 09.
1 10.
%n 11.
endeAber Abbruch ist hier nicht mit einem sofortigem Abbruch zu verwechseln, hier dauert der Abbruch schlappe 16 Sekunden!
01.
for /L %%n in (1,1,1000000) DO ( 02.
echo %%n - %time% 03.
goto :xx 04.
:xx 05.
echo %%n - %time% 06.
) 07.
echo ende 08.
------------ AUSGABE ------ 09.
1 - 22:28:05,73 10.
%n - 22:28:21,09 11.
endeUnd dann gibt es da noch ... und ... (eben das was in "FOR extrem" stehen sollte)
Grüße
jeb
Friemler schreibt am 26.11.2010 um 11:23:48 Uhr
Hi @all,
zunächst mal "Danke!" an alle für Eurer Lob, freut mich, dass das Tutorial so gut aufgenommen wurde.
@jeb
Danke für Deine Anregungen. Natürlich hast Du vollkommen recht, mein Tutorial ist keine vollständige Abhandlung zur FOR-Schleife. Deine Anmerkung zum Thema "Verwendung von verzögert erweiterten Variablen im Kopf der FOR-Schleife" gehört aber auf jeden Fall noch rein. Evtl. auch die Sache mit den GOTO-Befehlen.
Bei der Erstellung der Anleitung dachte ich eher an die Batch-Neulinge, die zunächst mal auf der Suche nach grundlegenden, verständlich und ausführlich dargestellten Informationen zu FOR sind. Das, was Dir vorschwebt, ist eher was für ein separates Tutorial "FOR for Runaways", vor allem weil das Tut in der jetzigen Form schon eine beträchtliche Länge hat.
Wie Du Dir vorstellen kannst, war die Erstellung des Tutorials schon etwas zeitaufwändig. Da ich mich z.Z. erstmal um ein anderes Projekt kümmern muss, falle ich im Moment als Autor für ein weiterführendes Tutorial aus. Aber Du hast ja schon mit Deinen Batch-Secrets bewiesen, dass Du für so etwas ebenfalls der richtige Mann wärst. Vielleicht lesen wir demnächst was von Dir zu dem Thema?
Gruß
Friemler
zunächst mal "Danke!" an alle für Eurer Lob, freut mich, dass das Tutorial so gut aufgenommen wurde.
@jeb
Danke für Deine Anregungen. Natürlich hast Du vollkommen recht, mein Tutorial ist keine vollständige Abhandlung zur FOR-Schleife. Deine Anmerkung zum Thema "Verwendung von verzögert erweiterten Variablen im Kopf der FOR-Schleife" gehört aber auf jeden Fall noch rein. Evtl. auch die Sache mit den GOTO-Befehlen.
Bei der Erstellung der Anleitung dachte ich eher an die Batch-Neulinge, die zunächst mal auf der Suche nach grundlegenden, verständlich und ausführlich dargestellten Informationen zu FOR sind. Das, was Dir vorschwebt, ist eher was für ein separates Tutorial "FOR for Runaways", vor allem weil das Tut in der jetzigen Form schon eine beträchtliche Länge hat.
Wie Du Dir vorstellen kannst, war die Erstellung des Tutorials schon etwas zeitaufwändig. Da ich mich z.Z. erstmal um ein anderes Projekt kümmern muss, falle ich im Moment als Autor für ein weiterführendes Tutorial aus. Aber Du hast ja schon mit Deinen Batch-Secrets bewiesen, dass Du für so etwas ebenfalls der richtige Mann wärst. Vielleicht lesen wir demnächst was von Dir zu dem Thema?
Gruß
Friemler
jeb-the-batcher schreibt am 08.06.2011 um 00:11:27 Uhr
Hallo mal wieder,
da ich in der Zwischenzeit noch ein wenig dazu gelernt habe, noch ein paar Infos.
EOL bezeichnet den End Of Line Buchstaben, wie der Name schon sagt gibt es nur einen, aber es gibt IMMER einen, sprich man kann EOL nicht einfach auf <Leer> setzen.
ein FOR /F "EOL=" ... bewirkt, dass EOL jetzt auf ein " gesetzt wurde.
Das kann manchmal recht unpraktisch werden, da jede Zeile die mit dem EOL-Buchstaben beginnt weggeworfen wird.
Allerdings gibt es zwei Ausnahmen:
Wenn der EOL-Buchstabe auch in den DELIMS Buchstaben vorkommt, ist der EOL-Mechanismus wirkungslos.
Das ist schon ganz nett, hilft aber auch nichts wenn man DELIMS leer hat.
Dann hilft nur noch den EOL auf <Linefeed> zu setzen, er ist dann zwar nicht leer, aber das spielt keine Rolle, da <Linefeed> ohnehin das Zeilenende markiert,
Allerdings ist es nicht ganz trivial den EOL auch tatsächlich auf <Linefeed> zu setzen.
Sieht schlimm aus, aber nur weil man das <Linefeed> innerhalb von Anführungszeichen erzeugen muss (was eben nicht so direkt geht), nach der Ausgabe mit ECHO ON sieht es schon recht normal aus.
-------
Dann wäre da noch der Zugriff auf die Laufvariablen der FOR-Schleifen.
Wenn man z.b. mit einem call :func aus einer FOR-Schleife rausspringt kann man nicht mehr auf die Laufvariablen zugreifen, aber mit einem Trick gelingt es dann doch wieder.
Sprich in einer FOR-Schleife sind immer ALLE Laufvariabeln erreichbar(außer denen die doppelt vergeben wurden), auch wenn man sich in einer Funktion befindet.
Grüße
jeb
da ich in der Zwischenzeit noch ein wenig dazu gelernt habe, noch ein paar Infos.
EOL bezeichnet den End Of Line Buchstaben, wie der Name schon sagt gibt es nur einen, aber es gibt IMMER einen, sprich man kann EOL nicht einfach auf <Leer> setzen.
ein FOR /F "EOL=" ... bewirkt, dass EOL jetzt auf ein " gesetzt wurde.
Das kann manchmal recht unpraktisch werden, da jede Zeile die mit dem EOL-Buchstaben beginnt weggeworfen wird.
Allerdings gibt es zwei Ausnahmen:
Wenn der EOL-Buchstabe auch in den DELIMS Buchstaben vorkommt, ist der EOL-Mechanismus wirkungslos.
Das ist schon ganz nett, hilft aber auch nichts wenn man DELIMS leer hat.
Dann hilft nur noch den EOL auf <Linefeed> zu setzen, er ist dann zwar nicht leer, aber das spielt keine Rolle, da <Linefeed> ohnehin das Zeilenende markiert,
Allerdings ist es nicht ganz trivial den EOL auch tatsächlich auf <Linefeed> zu setzen.
01.
echo on 02.
FOR /F ^"tokens^=1^ EOL^=^ 03.
04.
^ delims^=^" %%a in (myfile.txt) do echo %%aSieht schlimm aus, aber nur weil man das <Linefeed> innerhalb von Anführungszeichen erzeugen muss (was eben nicht so direkt geht), nach der Ausgabe mit ECHO ON sieht es schon recht normal aus.
-------
Dann wäre da noch der Zugriff auf die Laufvariablen der FOR-Schleifen.
Wenn man z.b. mit einem call :func aus einer FOR-Schleife rausspringt kann man nicht mehr auf die Laufvariablen zugreifen, aber mit einem Trick gelingt es dann doch wieder.
01.
@echo off 02.
for %%s in ("s-content") do ( 03.
echo loop1 loopvar-s=%%s 04.
call :test 05.
) 06.
exit /b 07.
08.
:test 09.
echo test1 loopvar-s=%%s 10.
for %%a in ("a-content") do ( 11.
echo loop2 loopvar-a=%%a 12.
echo loop2 loopvar-s=%%s 13.
) 14.
exit /bGrüße
jeb
Skyemugen schreibt am 09.06.2011 um 09:17:25 Uhr
Aloha jeb,
das mit den Laufvariablen ist interessant, so könnte man alle Laufvariablen nutzen, ohne die Parameter (wie sonst genutzt call :test "%%s" + %1 im Ablauf) irgendwann shiften zu müssen.
Aber zum EOL ... ich bin auf den ersten Fall gespannt, wo auch diese Variante dann gegen die Wand fährt
ich persönlich verwende bisher wenn dann ein Zeichen, was ich glaube, dass es nicht vorkommen wird - z.B. EOL=€ oder EOL=¬
greetz André
das mit den Laufvariablen ist interessant, so könnte man alle Laufvariablen nutzen, ohne die Parameter (wie sonst genutzt call :test "%%s" + %1 im Ablauf) irgendwann shiften zu müssen.
Aber zum EOL ... ich bin auf den ersten Fall gespannt, wo auch diese Variante dann gegen die Wand fährt
greetz André
pieh-ejdsch schreibt am 15.08.2011 um 23:15:46 Uhr
moin,
Wie man Befehle innerhalb einer Forschleife zerbröselt um die vielen EscapeZeichen dazwischen-zu-Friemeln wissen wir ja. Der Fehlerteufel Freut sich wenn mal eines davon fehlt!
Generell kann man es so halten das der Befehl mit Verkettung anderer Befehle genauso wie der Test ohne die Forschleife geschrieben wird und in der ForSchleife nicht extra mit jedem zu escapenden Zeichen Versehen werden muss.
zB. beim Filtern und Zählen von Dateien
Vergleichbar ist das mit :
das mit der For in der For mit delayedexpansion ist ein Spezialfall - da müssen nur noch die Analyseschlüsselwörter = (Sonderzeichen) escaped werden.
Gruß Phil
Wie man Befehle innerhalb einer Forschleife zerbröselt um die vielen EscapeZeichen dazwischen-zu-Friemeln wissen wir ja. Der Fehlerteufel Freut sich wenn mal eines davon fehlt!
Die ganze Befehlsverkettung hat doch grad noch im Test ohne die drumherumgebastelte ForSchleife Funktioniert.
so oder ähnlich macht sich meist Frust breit und die Lust an der Stelle Weiterzumachen schwindet auch mit der Zeit wenn die Augen ermüden.Generell kann man es so halten das der Befehl mit Verkettung anderer Befehle genauso wie der Test ohne die Forschleife geschrieben wird und in der ForSchleife nicht extra mit jedem zu escapenden Zeichen Versehen werden muss.
den Befehl(satz) nicht nur in einfache AnführungsZeichen einschliessen sondern erst in Doppelte AnführungsZeichen und darum in Einfache einschliessen.
FOR /F ["Optionen"] %Variable IN ( ' " Befehl1 [Parameter] | Befehl X && Befehl Y " ' ) DO Befehl2 [Parameter]
FOR /F ["Optionen"] %Variable IN ( ' " Befehl1 [Parameter] | Befehl X && Befehl Y " ' ) DO Befehl2 [Parameter]
zB. beim Filtern und Zählen von Dateien
01.
setlocal 02.
set "User=x-Tra" 03.
for /f %%i in (' " Dir /b *.bc? 2>nul|findstr /v /r /c:"[.]bco$"|findstr /v /r /c:"^%User%[.]"|find /v /c "" 2>nul " ') do echo %%i andere Dateien VorhandenVergleichbar ist das mit :
cmd /c"Befehl" cmd /k"Befehl"01.
setlocal EnableDelayedExpansion 02.
set eins=1 03.
set zwei=2 04.
set drei=a 05.
set "vier=set /p .=hallo < nul" 06.
for /f "delims=" %%z in ( ' " FOR /F "tokens^=!eins! delims^=!zwei!" %%!drei! in (' " !vier! " ') do @echo %%a " ' ) do echo %%zGruß Phil
















