Top-Themen

AppleEntwicklungHardwareInternetLinuxMicrosoftMultimediaNetzwerkeOff TopicSicherheitSonstige SystemeVirtualisierungWeiterbildungZusammenarbeit

Aktuelle Themen

Administrator.de FeedbackApache ServerAppleAssemblerAudioAusbildungAuslandBackupBasicBatch & ShellBenchmarksBibliotheken & ToolkitsBlogsCloud-DiensteClusterCMSCPU, RAM, MainboardsCSSC und C++DatenbankenDatenschutzDebianDigitiales FernsehenDNSDrucker und ScannerDSL, VDSLE-BooksE-BusinessE-MailEntwicklungErkennung und -AbwehrExchange ServerFestplatten, SSD, RaidFirewallFlatratesGoogle AndroidGrafikGrafikkarten & MonitoreGroupwareHardwareHosting & HousingHTMLHumor (lol)Hyper-VIconsIDE & EditorenInformationsdiensteInstallationInstant MessagingInternetInternet DomäneniOSISDN & AnaloganschlüsseiTunesJavaJavaScriptKiXtartKVMLAN, WAN, WirelessLinuxLinux DesktopLinux NetzwerkLinux ToolsLinux UserverwaltungLizenzierungMac OS XMicrosoftMicrosoft OfficeMikroTik RouterOSMonitoringMultimediaMultimedia & ZubehörNetzwerkeNetzwerkgrundlagenNetzwerkmanagementNetzwerkprotokolleNotebook & ZubehörNovell NetwareOff TopicOpenOffice, LibreOfficeOutlook & MailPapierkorbPascal und DelphiPeripheriegerätePerlPHPPythonRechtliche FragenRedHat, CentOS, FedoraRouter & RoutingSambaSAN, NAS, DASSchriftartenSchulung & TrainingSEOServerServer-HardwareSicherheitSicherheits-ToolsSicherheitsgrundlagenSolarisSonstige SystemeSoziale NetzwerkeSpeicherkartenStudentenjobs & PraktikumSuche ProjektpartnerSuseSwitche und HubsTipps & TricksTK-Netze & GeräteUbuntuUMTS, EDGE & GPRSUtilitiesVB for ApplicationsVerschlüsselung & ZertifikateVideo & StreamingViren und TrojanerVirtualisierungVisual StudioVmwareVoice over IPWebbrowserWebentwicklungWeiterbildungWindows 7Windows 8Windows 10Windows InstallationWindows MobileWindows NetzwerkWindows ServerWindows SystemdateienWindows ToolsWindows UpdateWindows UserverwaltungWindows VistaWindows XPXenserverXMLZusammenarbeit

Tutorial zur FOR-Schleife

Anleitung Entwicklung Batch & Shell

Mitglied: Friemler

Friemler (Level 2) - Jetzt verbinden

24.11.2010, aktualisiert 19.09.2016, 114118 Aufrufe, 13 Kommentare, 14 Danke

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.



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]
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":
01.
FOR %%Variable IN (Satz) DO ( 
02.
  Befehl1 [Parameter] 
03.
  Befehl2 [Parameter] 
04.
)
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.


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.
)
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.

Warum ist das so? 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 ""
ü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:
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.
)
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.


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]
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):
for %i in ("Hallo Welt" "*.txt" Hallo Welt "E:\My Scripts\*.bat") do @echo %i
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:
"Hallo Welt" 
Test.txt 
MeinText.txt 
Hallo 
Welt 
E:\My Scripts\Backup.bat 
E:\My Scripts\RDir.bat
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
dir /b "E:\My Scripts\*.bat"
Die Anführungszeichen um Dateimasken werden nicht ausgegeben. Zeichenketten und Dateimasken lassen sich gemischt angeben.

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.
endlocal
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.


Der 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 %i
Wenn sich im Verzeichnis E:\ die Verzeichnisse My Scripts und My Docs befinden, ist die Ausgabe folgende:
E:\My Scripts 
E:\My Docs

Der 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 %i
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:
E:\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]
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
for /r "E:\Archiv" %i in (.) do @echo %~fi
die folgende Ausgabe:
E:\Archiv 
E:\Archiv\Audio
Die 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 %i
angewendet wird, ist die Ausgabe folgende:
E:\Archiv\Audio
Das Ergebnis ist also gleichwertig zu
dir /s /b /a:d E:\Archiv

Die Variante zur Erzeugung von Zahlenfolgen, FOR /L

FOR /L %Variable IN (Start,Schritt,Ende) DO Befehl [Parameter]
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,
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%%i
liefert folgende Ausgabe:
file1=E:\Archiv\Audio\Test1.mp3 
file2=E:\Archiv\Audio\Test2.mp3 
file3=E:\Archiv\Audio\Track01.mp3 
file4=E:\Archiv\Audio\Track02.mp3
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.


Die Mehrzweck-Variante, FOR /F

FOR /F ["Optionen"] %Variable IN (Ausdruck) DO Befehl [Parameter]
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 Eierlegende Wollmilchsau. 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.


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 sein (Hallo Biber!). Wer weiss, wie's geht, möge das bitte posten. [EDIT] pieh-ejdsch hat inzwischen hier veröffentlicht, wie es geht. [/EDIT]


Zeilenendezeichen (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). Alle Zeichenketten/(Datei-)Zeilen, die mit dem so definierten Zeichen beginnen, werden komplett ignoriert. 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. [EDIT] jeb-the-batcher stellt in diesem Kommentar noch einen Trick in Zusammenhang mit EOL= zur Verfügung. [/EDIT]


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
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:
 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                     
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):
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))
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:
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 0
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.


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]
oder bei Verwendung der Option USEBACKQ
FOR /F "usebackq[ Optionen]" %Variable IN ('Zeichenkette') DO Befehl [Parameter]
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.
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%
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.
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 /b
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
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]
oder bei Verwendung der Option USEBACKQ
FOR /F "usebackq[ Optionen]" %Variable IN ("Dateiname"[ ...]]) DO Befehl [Parameter]
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. Wie das dennoch geht zeigt Beispiel 7a im Abschnitt Praxistipps.

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 /b
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,
;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\Versuch
sind nach Ausführung von Zeile 13 in obigem Skript die Variablen Source, Destination, ExcludeFiles und ExcludeDirs mit den angegebenen Werten belegt.


Verarbeiten 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]
oder bei Verwendung der Option USEBACKQ
FOR /F "usebackq[ Optionen]" %Variable IN (`Befehl1 [Parameter]`) DO Befehl2 [Parameter]
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. Wie das dennoch geht zeigt Beispiel 7b im Abschnitt Praxistipps.

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]
oder bei Verwendung der Option USEBACKQ
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 ^=
, Komma ^,
^ 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%
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:
<Ein String 
<
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:
0:<Ein String 
13:<
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.


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 frei
Die 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\
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.
for %f in ("E:\Test\*.txt") do @(for /f %d in ("%~tf") do @echo %~nf vom %d)
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.


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 ende
Die Ausgabe ist folgende:
%n 
ende
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).
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 ende
Die Ausgabe ist z.B. folgende:
1 - 16:48:54,93 
%n - 16:49:05,43 
ende
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.


Praxistipps

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 %f
ist kürzer und schneller als
for /f "delims=" %f in ('dir /b /a:-d E:\Test') do @echo %f
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
for /r E:\Test %d in (.) do @echo %~fd
liefert ein ähnliches Ergebnis wie
for /f "delims=" %d in ('dir /s /b /a:d E:\Test') do @echo %d
Die FOR /R Variante ist kürzer und schneller und es wird auch das Basisverzeichnis E:\Test gelistet.


3. Auch
for /r E:\Test %d in (..) do @echo %~fd
und
for /f "delims=" %d in ('dir /s /b /a:d E:\Test') do @echo %~dpd
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.
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%
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 einließt und dementsprechend Skript-Variablen setzt. Nachteilig an der Sache ist, daß 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

6. Um die Inhalte zweier Dateien zeilenweise zu mischen kann folgender Code verwendet werden:
01.
@echo off 
02.
setlocal enabledelayedexpansion 
03.
 
04.
set "InFile1=.\Test1.txt" 
05.
set "InFile2=.\Test2.txt" 
06.
set "OutFile=.\Merged.txt" 
07.
 
08.
(for /f "tokens=1* delims=:" %%a in ('findstr /n "^" "%InFile1%"') do ( 
09.
   set "line=" 
10.
   set /p "line=" 
11.
   echo.%%b 
12.
   echo.!line! 
13.
)) <"%InFile2%" >"%OutFile%"
Bei jedem Schleifendurchlauf wird aus jeder der beiden Eingabedateien jeweils eine Zeile gelesen.

Der FINDSTR-Befehl im Kopf der FOR-Schleife ließt wegen dem Parameter "^" alle Zeilen mit einem Anfang (dadurch werden alle Zeilen gelesen, auch leere) aus der ersten Eingabedatei. Durch den Parameter /n schreibt er vor jede Zeile die Zeilennummer gefolgt von einem Doppelpunkt. Der Doppelpunkt wird mit delims=: als Trennzeichen für den Tokenizer der FOR-Schleife festgelegt. Durch tokens=1* wird der vorgegebenen Laufvariablen %%a die Zeilennummer und der durch * automatisch erzeugten Laufvariablen %%b der Zeileninhalt zugewiesen.

Der Befehl set /p "line=" in Zeile 10 ließt die zweite Eingabedatei ein, denn diese wird in Zeile 13 des Scripts mit <"%InFile2%" der gesamten FOR-Schleife als Eingabedatei zugewiesen. Damit bei einer leeren Zeile in der zweiten Eingabedatei auch eine leere Zeile in der Ausgabedatei erscheint, wird in Zeile 9 die Variable gelöscht, die den Zeileninhalt aufnimmt.

Durch die Punkte nach den ECHO-Befehlen in den Zeilen 11 und 12 werden im Fall, dass eine der Variablen leer ist, tatsächlich Leerzeilen in die Ausgabedatei geschrieben. Ohne den Punkt nach dem ECHO-Befehl würde sonst die Meldung ECHO ist ausgeschaltet (OFF). in die Ausgabedatei geschrieben.

Falls die erste Eingabedatei weniger Zeilen als die zweite hat, werden aus der zweiten Eingabedatei nur so viele Zeilen gelesen, wie in der ersten Eingabedatei vorhanden sind.

Wenn die zweite Eingabedatei weniger Zeilen als die erste hat, werden für die fehlenden Zeilen Leerzeilen in die Ausgabedatei geschrieben.


7a. Eine Datei incl. Leerzeilen einlesen
01.
@echo off & setlocal 
02.
 
03.
set "InFile=.\Test1.txt" 
04.
 
05.
for /f "tokens=1* delims=:" %%a in ('findstr /n "^" "%InFile%"') do ( 
06.
  echo.%%b 
07.
)
Das Einlesen der Datei übernimmt der FINDSTR-Befehl. Damit können mit Hilfe sehr rudimentärer Regular Expressions Zeilen in Dateien gesucht werden, auf die das angegebene Suchmuster passt. Das Zeichen ^ steht für den Zeilenanfang. Dementsprechend passt das Suchmuster auf alle Zeilen. Durch den Parameter /n stellt FINDSTR jeder gefundenen Zeile die Zeilennummer gefolgt von einem Doppelpunkt voran. Der Doppelpunkt wird mit delims=: als Trennzeichen vereinbart. Die Zeilen der Eingabedatei werden wegen tokens=1* in zwei Tokens zerlegt. Das erste (in Laufvariable %%a, hier nicht verwendet) enthält die Zeilennummer und das zweite (in der automatisch erzeugten Laufvariable %%b) den Rest der Zeile nach dem Trennzeichen, also dem Doppelpunkt. Weil zwischen dem ECHO-Befehl und der Laufvariablen %%b ein Punkt steht, wird eine Leerzeile ausgegeben, wenn %%b nichts enthält, wenn also eine Leerzeile aus der Datei eingelesen wurde.

Der Code funktioniert übrigens auch einwandfrei, wenn die Datei Sonderzeichen enthält, die unter gewöhnlichen Umständen vom Batchscript-Interpreter als Steuerzeichen/Befehl angesehen werden. Der Inhalt der Laufvariablen von FOR-Schleifen wird NICHT interpretiert sondern als normaler Text angesehen.


7b. Die Ausgabe eines Befehls incl. Leerzeilen verarbeiten
01.
@echo off & setlocal 
02.
 
03.
set "InFile=.\Test1.txt" 
04.
 
05.
for /f "tokens=1* delims=:" %%a in ('type "%InFile%" ^| findstr /n "^"') do ( 
06.
  echo.%%b 
07.
)
Hier wird die Datei mit Hilfe des TYPE-Befehls eingelesen und über eine Pipe an den FINDSTR-Befehl geschickt. Statt TYPE könnte auch ein beliebiges anderes Kommando verwendet werden, z.B. DIR. Die Ausgabe von Leerzeilen wird durch den gleichen Trick wie in Beispiel 7a erledigt.

Der TYPE-Befehl ist übrigens dazu in der Lage, den Inhalt von Unicode-Dateien bei der Ausgabe in die aktuell eingestellte Codepage der Konsole zu konvertieren.


8. Laufvariablen einer FOR-Schleife in einem Unterprogramm verwenden
01.
@echo off & setlocal 
02.
 
03.
set "InFile=.\Test1.txt" 
04.
 
05.
for /f "tokens=1* delims=:" %%a in ('findstr /n "^" "%InFile%"') do ( 
06.
  call :SubRoutine 
07.
08.
 
09.
exit /b 
10.
 
11.
 
12.
:SubRoutine 
13.
  for %%z in (DummyLoop) do ( 
14.
    echo.%%b 
15.
16.
exit /b
Als Grundgerüst wird hier nochmals der Code aus Beispiel 7 verwendet. Im Unterschied dazu wird die Ausgabe jedoch im Unterprogramm SubRoutine erledigt. Das besteht nur aus einer Dummy-FOR-Schleife, die lediglich den Zweck hat, die Verwendung von FOR-Laufvariablen zu erlauben - die Laufvariable %%b der FOR-Schleife aus dem Hauptprogramm!

Diese Technik sollte man zwar nur im äußersten Notfall benutzen (evtl. funktioniert dieser Trick in zukünftigen Versionen von CMD.exe nicht mehr), aber es ist gut zu wissen, dass diese Möglichkeit existiert.


9. Beschleunigung von FOR-Schleifen, die Ausgaben in eine Datei schreiben
01.
@echo off & setlocal 
02.
 
03.
set "OutFile=.\Eval.txt" 
04.
set "InFiles=.\*.log" 
05.
set "SearchPattern=Error" 
06.
 
07.
(for %%f in ("%InFiles%") do ( 
08.
   for /f "tokens=* delims=" %%l in ('findstr /i /c:"%SearchPattern%" "%%f"') do ( 
09.
     echo %%f: %%l 
10.
11.
)) > "%OutFile%"
Der Code ließt im aktuellen Verzeichnis alle Dateien mit der Erweiterung log ein und sucht in ihnen Zeilen, die den Begriff Error (ohne Berücksichtigung der Groß-/Kleinschreibung) enthalten. Gefundene Zeilen werden in eine Ausgabedatei geschrieben (mit dem vorangestellten Namen der Datei, aus der sie stammen).

Die Umleitung der Ausgabe in der letzten Zeile bezieht sich durch die Klammerung der beiden FOR-Schleifen auf den gesamten Codeblock innerhalb der Klammern. Dadurch muss die Ausgabedatei nur einmal geöffnet werden, was bei vielen Schreiboperationen den Scriptlauf sehr stark beschleunigt. Ich hatte schon Anwendungsfälle, wo durch diese Technik die Laufzeit von 15 Minuten auf 10 Sekunden verkürzt werden konnte.


10. Array-Simulation mit mehr als 31 Array-Elementen
In Beispiel 11 wurde bereits eine Array-Simulation gezeigt. Die Indizierung des Arrays wurde dort mittels der TOKENS-Option der FOR-Schleife durchgeführt, wodurch das Array aber höchstens 31 Elemente haben kann. Der folgende Schnipsel zeigt, wie man diese Begrenzung überwindet und wie man eine FOR-Schleife als auszuführenden Befehl in die Klammer einer äußeren FOR /F -Schleife schreibt.
01.
@echo off & setlocal 
02.
 
03.
::Array füllen 
04.
set "Array="E01";"E02";"E03";"E04";"E05";"E06";"E07";"E08";"E09";"E10";"E11";"E12";"E13";"E14"" 
05.
set "Array=%Array%;"E15";"E16";"E17";"E18";"E19";"E20";"E21";"E22";"E23";"E24";"E25";"E26";"E27"" 
06.
set "Array=%Array%;"E28";"E29";"E30";"E31";"E32";"E33";"E34";"E35";"E36";"E37";"E38";"E39";"E40"" 
07.
set "Array=%Array%;"E41";"E42";"E43";"E44";"E45"" 
08.
 
09.
::Auszulesenden Index und Ergebnisvariable initialisieren 
10.
set /a Idx=42 
11.
set "Item=" 
12.
 
13.
::Array-Element auslesen 
14.
for /f "tokens=1* delims=:" %%a in ('^(for %%a in ^(%Array%^) do @echo %%a^) ^| findstr /n "^"') do ( 
15.
  if %%a equ %Idx% ( 
16.
    set "Item=%%b" 
17.
18.
19.
 
20.
::Angehängtes Leerzeichen entfernen 
21.
for %%a in (%Item%) do set "Item=%%~a" 
22.
 
23.
::Ergebnis ausgeben 
24.
echo.%Item%
Das Array wird mit der inneren FOR-Schleife Zeile für Zeile ausgegeben. Der FINDSTR-Befehl setzt durch den Parameter /N Zeilennummern und einen Doppelpunkt vor jedes Array-Element. Zeilennummer und Zeileninhalt werden von der äußeren FOR-Schleife durch die Optionen tokens=1* delims=: voneinander separiert. In der Laufvariablen %%a ist die Zeilennummer und in der Laufvariablen %%b der Zeileninhalt gespeichert. Wenn die Zeilennummer gleich dem gewünschten Index ist, wurde das Element gefunden. Dummerweise hängt der Batchscript-Interpreter noch ein Leerzeichen an den Zeileninhalt in %%b, das durch die Konstruktion in Zeile 21 entfernt wird.
Mitglied: bastla
24.11.2010 um 22:55 Uhr
Hallo Friemler!

Gratuliere zu diesem sehr umfangreichen und sehr gelungenen Tutorial.

Grüße
bastla
Bitte warten ..
Mitglied: Nagus
25.11.2010 um 09:23 Uhr
Hi Friemler,

DANKE!

Gruß Nagus
Bitte warten ..
Mitglied: Florian.Sauber
25.11.2010 um 17:09 Uhr
Vorbildlich, Friemler! Daumen hoch!

LG Florian
Bitte warten ..
Mitglied: 90776
25.11.2010 um 21:19 Uhr
der Kandidat hat 100 Punkte


schön geschrieben

Grüsse
Switcher
Bitte warten ..
Mitglied: jeb-the-batcher
25.11.2010 um 22:14 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.

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 %%a
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.
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.
10.
%n 
11.
ende
Aber 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.
ende
Und dann gibt es da noch ... und ... (eben das was in "FOR extrem" stehen sollte)

Grüße
jeb
Bitte warten ..
Mitglied: Friemler
26.11.2010 um 11:23 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
Bitte warten ..
Mitglied: jeb-the-batcher
08.06.2011 um 00:11 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.
01.
echo on 
02.
FOR /F ^"tokens^=1^ EOL^=^ 
03.
 
04.
^ delims^=^" %%a in (myfile.txt) do echo %%a
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.

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 /b
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
Bitte warten ..
Mitglied: Skyemugen
09.06.2011 um 09:17 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é
Bitte warten ..
Mitglied: pieh-ejdsch
15.08.2011 um 23:15 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!
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]

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 Vorhanden
Vergleichbar ist das mit :
cmd /c"Befehl" 
cmd /k"Befehl"
das mit der For in der For mit delayedexpansion ist ein Spezialfall - da müssen nur noch die Analyseschlüsselwörter = (Sonderzeichen) escaped werden.
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 %%z
Gruß Phil
Bitte warten ..
Mitglied: Friemler
16.08.2011 um 11:09 Uhr
Hallo Phil,

danke für die neuen Erkenntnisse. Ein Hinweis darauf ist schon ins Tutorial eingebaut.

Gruß
Friemler
Bitte warten ..
Mitglied: Wolfsjux
01.04.2015, aktualisiert um 09:32 Uhr
Danke für die hervorragenden Beiträge. Hier noch ein hübsches Beispiel um festzustellen (z.B.: beim Autostart), welche Laufwerke aktuell angeschlossen sind:
01.
for %%x in (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) do if exist %%x:\ echo Das Laufwerk ist angeschlossen %%x
Gruß Wolf
Bitte warten ..
Mitglied: evinben
15.03.2016, aktualisiert um 18:48 Uhr
wäre es irgendwie möglich in delims= eine Trennzeichenkette zu erzwingen, also dass dann die dort eingegebenen Zeichen nur in der angegebenen Kombination 1:1 - sogar unter Berücksichtigung von Groß-Kleinschreiben - gültig werden?
Z. B. etwa "Gr." einzugeben und dabei nach der Zeichenkette "Gr." suchen und so die gefundene Strings in Tokens aufteilen? Weil so wäre es in manchen Texten sicherere/zuverlässiger (als bloß nach Punkt "." zu suchen, weil dieser im Text in vielen Zeilen öfters willkürlich vorkommt könnte und "G" und "r" einzeln geht so natürlich gar nicht, weil die Buchstaben fast überall im Text unsystematisch vorhanden sind)

Gruß
evinben
Bitte warten ..
Mitglied: bastla
15.03.2016, aktualisiert um 23:38 Uhr
Hallo evinben!

Per for /f lässt sich das nicht realisieren, aber sieh Dir vielleicht einmal "Musterlösungen: Dateien verschieben abhängig vom Teil des Dateinamens" (für Batch konkret: hier) dazu an ...

Grüße
bastla
Bitte warten ..
Neuester Wissensbeitrag
Internet

Unbemerkt - Telekom Netzumschaltung! - BNG - Broadband Network Gateway

(3)

Erfahrungsbericht von ashnod zum Thema Internet ...

Ähnliche Inhalte
Batch & Shell
gelöst CMD: icacls in for-Schleife (2)

Frage von Lowrider614 zum Thema Batch & Shell ...

Batch & Shell
gelöst Batch Problem bei einer For Schleife (2)

Frage von Juergen42 zum Thema Batch & Shell ...

Batch & Shell
gelöst For Schleife kaputt? (5)

Frage von Peter32 zum Thema Batch & Shell ...

Batch & Shell
Batch: Variable Expansion in einer FOR-Schleife (9)

Frage von .Sessl zum Thema Batch & Shell ...

Heiß diskutierte Inhalte
Windows Server
Outlook Verbindungsversuch mit Exchange (15)

Frage von xbast1x zum Thema Windows Server ...

Grafikkarten & Monitore
Tonprobleme bei Fernseher mit angeschlossenem Laptop über HDMI (11)

Frage von Y3shix zum Thema Grafikkarten & Monitore ...

Microsoft Office
Keine Updates für Office 2016 (11)

Frage von Motte990 zum Thema Microsoft Office ...