Tutorial zur FOR-Schleife
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
Möchte man mehr als einen Befehl in der FOR-Schleife ausführen lassen, kann man wie üblich die Befehle mit dem Operator
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
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
ü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
Die Variante
Damit lassen sich Zeichenketten und Dateinamensmasken verarbeiten.
Beispiel 1 (für die Kommandozeile):
Wenn sich im aktuellen Verzeichnis die Dateien
Wurde in
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
Beispiel 3 (für die Kommandozeile):
Wenn sich im Verzeichnis
Beispiel 4 (für die Kommandozeile):
Wenn sich im Verzeichnis
Enthält
der auch das Basisverzeichnis liefert. Unter DOS/Windows gibt es nämlich in jedem Verzeichnis zwei Standard-Einträge, die immer vorhanden sind:
Beispiel 5 (für die Kommandozeile):
Auf die eben beschriebene Verzeichnisstruktur angewendet, liefert der Befehl
die folgende Ausgabe:
Die Parameter
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
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
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
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
Die ersten 5 Zeilen dieser Ausgabe interessieren uns nicht, deshalb
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
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
Außer bei der Datei mit dem Namen "Mein Text.txt". Dieser Name enthält ein Leerzeichen, also ein Trennzeichen. Deshalb enthält
Beispiel 9 (für die Kommandozeile):
Endet die Liste der zu verwendenden Tokens mit einem Stern (
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
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
Die Auswertung von
Die Option
oder bei Verwendung der Option
Die Zeichenkette wird als eine Zeile einer Datei aufgefasst und entsprechend der Optionen
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
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
In Zeile 13 wird die erste Hex-Ziffer von links (ab dem Offset 0, 1 Zeichen) der Variablen
Die Zeilen 18 bis 28 suchen im Array
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
Übrigens: Der einfachste Hex=>Dec Konverter in Batchskript ist natürlich
oder bei Verwendung der Option
Die Angabe von mehreren Dateien ist möglich. Die Dateien werden zeilenweise ausgelesen. Jede einzelne Zeile wird entsprechend den Optionen
Bei der Version mit
Beispiel 12:
Die erste FOR-Schleife in Zeile 11 löscht die Variablen, deren Namen in der Variablen
Die FOR /F Schleife in Zeile 13 liest die Datei, deren Name in der Variablen
Die dritte FOR-Schleife in Zeile 15-20 prüft, ob alle Variablen, deren Namen in der Variablen
Wenn in Zeile 22 festgestellt wird, dass
Wenn die Datei
sind nach Ausführung von Zeile 13 in obigem Skript die Variablen
oder bei Verwendung der Option
Im Unterschied zur Version ohne
Wenn
oder bei Verwendung der Option
[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
Der FINDSTR-Befehl in der Klammer der FOR-Schleife in Zeile 10 durchsucht die Datei
Diese Ausgabe wird ja bekanntlich nicht angezeigt, sondern durch die FOR-Schleife verarbeitet. Als Trennzeichen zwischen Tokens ist durch
Übrigens: Der Suchstring des FINDSTR-Befehls, das Zeichen
Beispiel 14 (für die Kommandozeile):
Betrachten wir ein Verzeichnis, bei dem
Die folgende Tabelle gibt an, wie die Ausgabe zu jedem hinzugefügten Kürzel aussieht.
Achtung! Der Befehl
Die o.g. Kürzel/Operatoren können auch kombiniert werden. So liefert z.B.
und
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
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
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
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
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
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
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
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.
6. Um die Inhalte zweier Dateien zeilenweise zu mischen kann folgender Code verwendet werden:
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
Der Befehl
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
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
Das Einlesen der Datei übernimmt der
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
Hier wird die Datei mit Hilfe des
Der
8. Laufvariablen einer FOR-Schleife in einem Unterprogramm verwenden
Als Grundgerüst wird hier nochmals der Code aus Beispiel 7 verwendet. Im Unterschied dazu wird die Ausgabe jedoch im Unterprogramm
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
Der Code ließt im aktuellen Verzeichnis alle Dateien mit der Erweiterung
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
Das Array wird mit der inneren
11. Bestimmte Zeilen aus einer Datei extrahieren
In Beispiel 9 hatte ich erwähnt, dass man mit
In dem Schnipsel werden einige bereits vorgestellte Techniken verwendet (innere
Mit der inneren
Die Ausgabe der inneren
Hier muss jetzt alles links vom Gleichheitszeichen entfernt und die gewünschte Anzahl Zeilen bis zur Startzeile übersprungen werden. Das macht die äußere
Es gibt übrigens eine Einschränkung für das Script: Da die
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.
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":FOR %%Variable IN (Satz) DO (
Befehl1 [Parameter]
Befehl2 [Parameter]
)
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:setlocal
set "users=C:\Users"
set "home="
set /p "home=Geben Sie den Benutzernamen ein: "
if not "%home%"=="" (
set "op_dir=%users%\%home%"
dir "%op_dir%"
)
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 alsdir ""
Um zum gewünschten Ergebnis zu kommen, muss die verzögerte Variablenerweiterung aktiviert und benutzt werden. Das Beispiel muss dann so lauten:
setlocal enabledelayedexpansion
set "users=C:\Users"
set "home="
set /p "home=Geben Sie den Benutzernamen ein: "
if not "%home%"=="" (
set "op_dir=%users%\%home%"
dir "!op_dir!"
)
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]
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
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
Satz
zusammen mit einer Dateimaske ein Pfad angegeben, wird er bei der Ausgabe auch angezeigt. Das ist auch der Vorteil gegenüber dem Befehldir /b "E:\My Scripts\*.bat"
Beispiel 2:
@echo off & setlocal
cls
set "Dest=E:\Test"
set /p "FileName=Geben Sie einen Dateinamen ein: "
if "%FileName%" equ "" goto :EOF
:SelectAction
set /p "Action=Möchten Sie diese Datei kopieren oder verschieben (A für Abbruch) (K/V/A)? "
for %%i in (K V A) do if /i "%Action%" equ "%%i" goto :%%i
goto :SelectAction
:K
copy "%FileName%" "%Dest%"
goto :EOF
:V
move "%FileName%" "%Dest%"
goto :EOF
:A
endlocal
%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
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
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ürdir /s /b /a:d [[Laufwerk:]Pfad]
.
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
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
E:\Archiv\Audio
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]
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,
@echo off
setlocal enabledelayedexpansion
cls
set "cntr=0"
for %%i in ("E:\Archiv\Audio\*.*") do (
set /a "cntr+=1"
set "file!cntr!=%%i"
)
for /l %%i in (1,1,%cntr%) do set file%%i
file1=E:\Archiv\Audio\Test1.mp3
file2=E:\Archiv\Audio\Test2.mp3
file3=E:\Archiv\Audio\Track01.mp3
file4=E:\Archiv\Audio\Track02.mp3
%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]
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 vonAusdruck
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 OptionDELIMS=
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 OptionEOL=
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 OptionSKIP=
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 OptionTOKENS=
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
/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
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))
*
), 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
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]
USEBACKQ
FOR /F "usebackq[ Optionen]" %Variable IN ('Zeichenkette') DO Befehl [Parameter]
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.
::Entfernen von führenden Nullen bei Zahlen, damit sie
::in Berechnungen nicht als Oktalzahlen aufgefasst werden
@echo off & setlocal
cls
set /p "Number=Geben Sie eine Zahl mit oder ohne führende Nullen ein: "
for /f "tokens=* delims=0" %%n in ("%Number%") do set "Number=%%n"
set /a "Number=Number"
echo Die eingegebene Zahl ist %Number%
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.::Hex=>Dec Konverter mit Array-Simulation
@echo off & setlocal
set "HexDigits=0 1 2 3 4 5 6 7 8 9 A B C D E F"
set "ConvArray=0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"
set "Result=0"
cls
set /p "HexNum=Geben Sie eine hexadezimale Zahl ein (max. 8-stellig): "
:ConvLoop
set "Digit=%HexNum:~0,1%"
set "HexNum=%HexNum:~1%"
set "Index=1"
:ArrayLookup
for /f "tokens=%Index%" %%i in ("%HexDigits%") do (
if /i "%Digit%" equ "%%i" (
for /f "tokens=%Index%" %%j in ("%ConvArray%") do (
set "DecValue=%%j"
goto :CalcResult
)
)
)
set /a "Index+=1"
if %Index% leq 16 (goto :ArrayLookup) else (goto :ForbiddenChar)
:CalcResult
set /a "Result=Result*16+DecValue"
if "%HexNum%" neq "" goto :ConvLoop
echo Das dezimale Äquivalent dieser Zahl ist %Result%
exit /b
:ForbiddenChar
echo Es sind nur die Zeichen 0-9, a-f und A-F zur Eingabe erlaubt.
exit /b
%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]
USEBACKQ
FOR /F "usebackq[ Optionen]" %Variable IN ("Dateiname"[ ...]]) DO Befehl [Parameter]
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:
::Konfigurationsdatei einlesen
::und dadurch Skript-Variablen setzen
@echo off & setlocal
set "ConfigFile=C:\Programme\Backup\Backup.conf"
set "VarMissed=0"
set "Vars=Source Destination ExcludeFiles ExcludeDirs"
for %%v in (%Vars%) do set "%%v="
for /f "usebackq tokens=1,2 delims==" %%v in ("%ConfigFile%") do set "%%v=%%w"
for %%v in (%Vars%) do (
if not defined %%v (
call :MissingVar %%v
set "VarMissed=1"
)
)
if %VarMissed% equ 1 goto :ScriptEnd
.
.
.
:ScriptEnd
endlocal
exit /b
:MissingVar
echo Variable %1 ist in der Konfigurationsdatei nicht definiert.
exit /b
%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
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]
USEBACKQ
FOR /F "usebackq[ Optionen]" %Variable IN (`Befehl1 [Parameter]`) DO Befehl2 [Parameter]
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]
USEBACKQ
FOR /F "usebackq[ Optionen]" %Variable IN (`call "Befehl1" "Parameter"`) DO Befehl2 [Parameter]
Escapen von Steuerzeichen
WennBefehl1
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:
::Bestimmen der Länge einer Zeichenkette
@echo off & setlocal
set /p "Str=Geben Sie einen String ein: "
set "StrLen=0"
>"%TEMP%\strlen.txt" (echo ^<%Str%&echo ^<)
for /f "delims=:" %%l in ('findstr /o /b "<" "%TEMP%\strlen.txt"') do set /a "StrLen=%%l-3"
del "%TEMP%\strlen.txt"
echo Die Länge von %Str% ist %StrLen%
%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
<
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:<
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 |
for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~dF | E: |
for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~pF | \Test\ |
for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~nF | Doku-Test |
for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~xF | .txt |
for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~sF | E:\Test\Doku-T~1.txt |
for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~aF | --a------ |
for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~tF | 30.10.2010 18:52 |
for /f "delims=" %F in ('dir /b E:\Test\*.txt') do @echo %~zF | 8519 |
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 BeispieldateienE:\Test\
E:\Test\
%~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)
%~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.
@echo off
for /l %%n in (1,1,100) do (
echo %%n
goto :label
:label
echo %%n
)
echo ende
1
%n
ende
%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).
@echo off
for /l %%n in (1,1,1000000) do (
echo %%n - %time%
goto :xx
:xx
echo %%n - %time%
)
echo ende
1 - 16:48:54,93
%n - 16:49:05,43
ende
%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
for /f "delims=" %f in ('dir /b /a:-d E:\Test') do @echo %f
2. Der Code
for /r E:\Test %d in (.) do @echo %~fd
for /f "delims=" %d in ('dir /s /b /a:d E:\Test') do @echo %d
E:\Test
gelistet.3. Auch
for /r E:\Test %d in (..) do @echo %~fd
for /f "delims=" %d in ('dir /s /b /a:d E:\Test') do @echo %~dpd
E:\
).4. Beispiel 10 in einer Version, die die Eingabe von Zeichen, bei denen es sich nicht um Zahlen handelt, abfängt.
::Entfernen von führenden Nullen bei Zahlen, damit sie
::in Berechnungen nicht als Oktalzahlen aufgefasst werden
@echo off & setlocal
:InputNumber
cls
set /p "Number=Geben Sie eine Zahl mit oder ohne führende Nullen ein: "
for /f %%c in ('echo %Number%^|findstr "[^0-9]"') do goto :InputNumber
for /f "tokens=* delims=0" %%n in ("%Number%") do set "Number=%%n"
set /a "Number=Number"
echo Die eingegebene Zahl ist %Number%
%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.
::Konfigurationsdatei vom ANSI- ins ASCII-Format konvertieren
set "ConfigFile=C:\Programme\Backup\Backup.conf"
set "TempConfigFile=%Temp%\Backup.conf"
::Evtl. vorhandene temporäre Konfigurationsdatei löschen
type NUL > "%TempConfigFile%"
::ANSI-Codepage aktivieren
chcp 1252 > NUL
::Datei zeilenweise konvertieren
for /f "usebackq delims=" %%l in ("%ConfigFile%") do (
set "line=%%l"
call :Convert2ASCII
)
::Konfiguration einlesen
for /f "usebackq tokens=1,2 delims==" %%v in ("%TempConfigFile%") do set "%%v=%%w"
::Temporäre Konfigurationsdatei löschen
del "%TempConfigFile%"
.
.
.
exit /b
:Convert2ASCII
::auf DOS-Codepage umschalten
chcp 850 > NUL
::Eine Zeile schreiben
>>"%TempConfigFile%" echo %line%
exit /b
6. Um die Inhalte zweier Dateien zeilenweise zu mischen kann folgender Code verwendet werden:
@echo off
setlocal enabledelayedexpansion
set "InFile1=.\Test1.txt"
set "InFile2=.\Test2.txt"
set "OutFile=.\Merged.txt"
(for /f "tokens=1* delims=:" %%a in ('findstr /n "^" "%InFile1%"') do (
set "line="
set /p "line="
echo.%%b
echo.!line!
)) <"%InFile2%" >"%OutFile%"
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
@echo off & setlocal
set "InFile=.\Test1.txt"
for /f "tokens=1* delims=:" %%a in ('findstr /n "^" "%InFile%"') do (
echo.%%b
)
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
@echo off & setlocal
set "InFile=.\Test1.txt"
for /f "tokens=1* delims=:" %%a in ('type "%InFile%" ^| findstr /n "^"') do (
echo.%%b
)
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
@echo off & setlocal
set "InFile=.\Test1.txt"
for /f "tokens=1* delims=:" %%a in ('findstr /n "^" "%InFile%"') do (
call :SubRoutine
)
exit /b
:SubRoutine
for %%z in (DummyLoop) do (
echo.%%b
)
exit /b
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
@echo off & setlocal
set "OutFile=.\Eval.txt"
set "InFiles=.\*.log"
set "SearchPattern=Error"
(for %%f in ("%InFiles%") do (
for /f "tokens=* delims=" %%l in ('findstr /i /c:"%SearchPattern%" "%%f"') do (
echo %%f: %%l
)
)) > "%OutFile%"
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.@echo off & setlocal
::Array füllen
set "Array="E01";"E02";"E03";"E04";"E05";"E06";"E07";"E08";"E09";"E10";"E11";"E12";"E13";"E14""
set "Array=%Array%;"E15";"E16";"E17";"E18";"E19";"E20";"E21";"E22";"E23";"E24";"E25";"E26";"E27""
set "Array=%Array%;"E28";"E29";"E30";"E31";"E32";"E33";"E34";"E35";"E36";"E37";"E38";"E39";"E40""
set "Array=%Array%;"E41";"E42";"E43";"E44";"E45""
::Auszulesenden Index und Ergebnisvariable initialisieren
set /a Idx=42
set "Item="
::Array-Element auslesen
for /f "tokens=1* delims=:" %%a in ('^(for %%a in ^(%Array%^) do @echo %%a^) ^| findstr /n "^"') do (
if %%a equ %Idx% (
set "Item=%%b"
)
)
::Angehängtes Leerzeichen entfernen
for %%a in (%Item%) do set "Item=%%~a"
::Ergebnis ausgeben
echo.%Item%
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.11. Bestimmte Zeilen aus einer Datei extrahieren
In Beispiel 9 hatte ich erwähnt, dass man mit
FOR /F
zwar eine bestimmte Anzahl Zeilen vom Dateianfang aus überspringen kann, es jedoch keine Möglichkeit gibt, eine bestimmte Anzahl Zeilen am Ende einer Datei zu ignorieren oder nur eine bestimmte Anzahl Zeilen der Datei zu verarbeiten. Manchmal wäre es jedoch schon praktisch, ab einer bestimmten Dateizeile einen Block von Zeilen zu extrahieren und nur diese Zeilen weiterzuverarbeiten. Genau das leistet das folgende Script.@echo off & setlocal
::Eingabedatei sowie Start- und Endzeile festlegen
set "InFile=Logfile.log"
set "StartLine=5"
set "StopLine=10"
::Der Wert für die Startzeile muss korrigiert werden
set /a StLn=StartLine-1
::Datei bis zur angegebenen Endzeile einlesen, die gewünschte Anzahl Zeilen am
::Anfang ignorieren und nur die gewünschten Zeilen ausgeben
for /f "skip=%StLn% tokens=1* delims==" %%a in ('^(for /l %%a in ^(1,1,%StopLine%^) do @^(set "TmpVar=" ^& set /p "TmpVar=" ^& set TmpVar 2^>NUL^) ^|^| echo TmpVar^=^) ^< "%InFile%"') do (
echo(%%b
)
FOR
-Schleife im Kopf einer äußeren FOR
-Schleife (Praxistipp 10), Einlesen einer Datei innerhalb einer FOR
-Schleife durch Eingabeumleitung für die gesamte Schleife (Praxistipp 6) und Ausgabe von Variableninhalten innerhalb einer FOR
-Schleife durch den SET
-Befehl (Beispiel 7)).Mit der inneren
FOR
-Schleife wird die Datei bis zur angegebenen Endzeile eingelesen. Jede eingelesene Zeile wird mit set /p "TmpVar="
an die Umgebungsvariable TmpVar
zugewiesen, worauf der Variableninhalt mit set TmpVar
ausgegeben wird. Beim Einlesen einer Leerzeile wird der Wert von TmpVar
jedoch nicht verändert, der bisherige Wert bleibt erhalten. Damit der Inhalt der vorherigen Zeile nicht nochmals ausgegeben wird, wird TmpVar
vor der Zuweisung des Inhalts einer Zeile mit set "TmpVar="
gelöscht. Damit in diesem Fall bei der Ausgabe des Variablenwerts keine Fehlermeldung ausgegeben wird (á la Umgebungsvariable existiert nicht), wird der Standard-Fehlerkanal auf das NUL-Device umgeleitet. Außerdem kann die fehlgeschlagene Ausführung von set TmpVar
dazu benutzt werden, den Text TmpVar=
auszugeben, damit die äußere FOR
-Schleife an dieser Stelle eine Leerzeile ausgibt.Die Ausgabe der inneren
FOR
-Schleife sieht ungefähr so aus:TmpVar=Inhalt von Zeile 1
TmpVar=Inhalt von Zeile 2
TmpVar= <- Hier ist in der Eingabedatei eine Leerzeile
TmpVar=Inhalt von Zeile 4
...
FOR
-Schleife, indem der Tokenizer mit den Optionen tokens=1*
und delims==
entsprechend eingestellt wird und mit der Option skip=...
die nicht gewünschten Zeilen herausgefiltert werden.Es gibt übrigens eine Einschränkung für das Script: Da die
FOR /F
-Schleife bei der Option SKIP=
den Wert 0 nicht toleriert, kann man die Quelldatei nicht von Zeile 1 bis X sondern nur mindestens von Zeile 2 an verarbeiten. Für die Verarbeitung von Zeile 1 bis X muss die äußere FOR
-Schleife entfernt und die innere entsprechend umgeschrieben werden, damit sie auch Stand-alone läuft (Escape-Zeichen entfernen).
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 155681
Url: https://administrator.de/contentid/155681
Ausgedruckt am: 21.11.2024 um 15:11 Uhr
28 Kommentare
Neuester Kommentar
der Kandidat hat 100 Punkte
schön geschrieben
Grüsse
Switcher
schön geschrieben
Grüsse
Switcher
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.
setlocal DisableDelayedExpansion
FOR /F "delims=*" %%a in ("hallo ^caret!") do (
echo %%a
set var=%%a
)
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
setlocal DisableDelayedExpansion
FOR /F "delims=*" %%a in ("hallo ^caret!") do (
set var=%%a
setlocal EnableDelayedExpansion
set "var=!var:h=b!"
echo !var!
endlocal
)
Oder was geht so? Oder wie und wann wirkt die Expansion im FOR Befehl?
setlocal EnableDelayedExpansion
set eins=1
set zwei=2
set drei=a
set "vier=set /p .=hallo < nul"
FOR /F "tokens=!eins! delims=!zwei!" %%!drei! in ('!vier!') do echo %%a
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.
for /L %%n in (1,1,100) DO (
echo %%n
goto :label
:label
echo %%n
)
echo ende
------------ AUSGABE ------
1
%n
ende
Aber Abbruch ist hier nicht mit einem sofortigem Abbruch zu verwechseln, hier dauert der Abbruch schlappe 16 Sekunden!
for /L %%n in (1,1,1000000) DO (
echo %%n - %time%
goto :xx
:xx
echo %%n - %time%
)
echo ende
------------ AUSGABE ------
1 - 22:28:05,73
%n - 22:28:21,09
ende
Und dann gibt es da noch ... und ... (eben das was in "FOR extrem" stehen sollte)
Grüße
jeb
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.
echo on
FOR /F ^"tokens^=1^ EOL^=^
^ 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.
@echo off
for %%s in ("s-content") do (
echo loop1 loopvar-s=%%s
call :test
)
exit /b
:test
echo test1 loopvar-s=%%s
for %%a in ("a-content") do (
echo loop2 loopvar-a=%%a
echo loop2 loopvar-s=%%s
)
exit /b
Grüße
jeb
Aloha jeb,
das mit den Laufvariablen ist interessant, so könnte man alle Laufvariablen nutzen, ohne die Parameter (wie sonst genutzt
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.
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 shift
en 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é
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
setlocal
set "User=x-Tra"
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"
setlocal EnableDelayedExpansion
set eins=1
set zwei=2
set drei=a
set "vier=set /p .=hallo < nul"
for /f "delims=" %%z in ( ' " FOR /F "tokens^=!eins! delims^=!zwei!" %%!drei! in (' " !vier! " ') do @echo %%a " ' ) do echo %%z
Gruß Phil
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
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
Hallo evinben!
Per
Grüße
bastla
Per
for /f
lässt sich das nicht realisieren, aber sieh Dir vielleicht einmal "hier) dazu an ...Grüße
bastla
Hallo,
das Tutorial zur For-Schleife ist echt super.
Nun habe ich als anfänger etwas herumexperimentiert.
Das Beispiel 11 funktioniert bei mir nicht.
for /f "tokens=%Index%" %%i in ("%HexDigits%") do (
Wenn innerhalb des Schleifenkörpers das if-statement steht steigt das Skript aus.
Ich nutze Windows 7.
Habe auch das Skript 1:1 kopiert. Es steigt ebenfalls im Schleifenkörper bei if aus.
Woran kann das liegen?
Muss ich da möglicherweise noch Einstellungen in cmd vornehmen?
Ueber einen Tipp würde ich mich sehr freuen.
Gruss Hein_nieH
das Tutorial zur For-Schleife ist echt super.
Nun habe ich als anfänger etwas herumexperimentiert.
Das Beispiel 11 funktioniert bei mir nicht.
for /f "tokens=%Index%" %%i in ("%HexDigits%") do (
Wenn innerhalb des Schleifenkörpers das if-statement steht steigt das Skript aus.
Ich nutze Windows 7.
Habe auch das Skript 1:1 kopiert. Es steigt ebenfalls im Schleifenkörper bei if aus.
Woran kann das liegen?
Muss ich da möglicherweise noch Einstellungen in cmd vornehmen?
Ueber einen Tipp würde ich mich sehr freuen.
Gruss Hein_nieH
... mist, zu frueh gefreut ( bei Beispiel 11)
for /f "tokens=%Index%" %%i in ("%HexDigits%") do (
if /i"%Digit%" EQU "%%i" (
beim IF Statement steigt das Script aus.
Weiss jemand warum
Lasse ich das Leerzeichen weg, also if/i dann kommt die Fehlermeldung, dass if nicht verarbeitet werden kann.
Wer kann mir hier weiterhelfen?
for /f "tokens=%Index%" %%i in ("%HexDigits%") do (
if /i"%Digit%" EQU "%%i" (
beim IF Statement steigt das Script aus.
Weiss jemand warum
Lasse ich das Leerzeichen weg, also if/i dann kommt die Fehlermeldung, dass if nicht verarbeitet werden kann.
Wer kann mir hier weiterhelfen?
Hallo Hein,
verwendest du xxx.bat scripts.
Bei mir geht es noch so wie in Win8, jetzt auch in Win10! Win7 weiß ich nicht.
@echo off
rem Dos-Befehls-Sammlung, die funktioniert, erstellt von Jung 15.5.15
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 gibt es %%x
pause
:Ende
Echo * Ende der Procedur xxxxxxxx Ende
Gruß Wolf
verwendest du xxx.bat scripts.
Bei mir geht es noch so wie in Win8, jetzt auch in Win10! Win7 weiß ich nicht.
@echo off
rem Dos-Befehls-Sammlung, die funktioniert, erstellt von Jung 15.5.15
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 gibt es %%x
pause
:Ende
Echo * Ende der Procedur xxxxxxxx Ende
Gruß Wolf
if /i"%Digit%" EQU "%%i" (
Hinter dem /i fehlt das Leerzeichen.Gruß
Hallo allerseits, gehöre mit meinem Festhalten an Windows XP und meinem 16-Bit-PowerBasic-Compiler aus den 1990ern wahrscheinlich zu den Dinosauriern in dieser Runde, versuche mich aber wie schon zu DOS-Zeiten immer wieder gern darin, Probleme allein mit den asketischen Bordmitteln der jeweiligen Kommandozeilenumgebung zu lösen, manchmal dann auch mit einer quasi die letzten Meter zum Erfolg überbrückenden kleinen EXE aus meinen PowerBasic-Compiler, wobei Windows' CMD.EXE allerdings wirklich so einiges mehr zu bieten hat als die gute alte COMMAND.COM. Und aus diesem Erfahrungshorizont heraus hier noch ein paar vielleicht nützliche Anmerkungen und Ergänzungen:
1) In Friemlers tollen Tutorial (Hut ab!) lesen wir unter Pkt.5: "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, [...] 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." Oder eben _nicht_ mit dem Notepad, weil das nur ANSI oder UTF-8/UTF-16 kann! Habe also, als ich irgendwann auf Windows umsteigen musste, anfangs auch lange nach so etwas gesucht, also einem im Idealfall zu Windows' eigenen Bordmitteln gehörenden Editor, der sowohl ANSI als auch ASCII kann, und das ist eben nicht das Windows-Notepad, sondern das Windows-Wordpad! Das kann nämlich ANSI, UTF-16, aber auch ASCII und, wenn man 'n bissel was formatieren möchte, RTF (und das nicht nur schreiben, sondern auch lesen, s.u.)!
Also quasi das ideale Bindeglied zwischen DOS- und Windows-Texten. So dass, wenn ich seitdem irgendwelche Batchdateien schreibe, ich das zunächst einmal immer mit dem Wordpad im RTF-Format tue (und dabei, um irgendwas im Quelltext hervorzuheben, auch schön mit Farben, Fett, Kursiv und Unterstreichen arbeiten kann), und nachdem ich das dann im RTF-Format abgespeichert hab, speichere ich's mit "Speichern unter" jedesmal auch nochmal mit der Option "Textdokument - MS-DOS-Format" als ausführbare Batch mit der von mir dabei vorzugebenden Extension BAT oder CMD (weil das Wordpad selber natürlich immer erstmal TXT vorschlägt). Ist für mich, seitdem ich draufgekommen bin, der Königsweg, umso mehr, als ich dadurch auch immer zwei Versionen meiner Batch habe, eine reich illustrierte Dokumentation in Form der RTF, und dann nochmal die ausführbare Datei als BAT oder CMD.
2) Zu dem Pkt. "Erweiterung von Laufvariablen" muss ich, was "%~sI" angeht, leider was korrigieren: Diese Erweiterung funktioniert so wie beschrieben leider nur, wenn alle im Suchpfad vorkommenden Verzeichnis- und Dateinamen DOS-konform sind, also zB. keine der neuerdings so beliebten (langen) Gedankenstriche oder andere Zeichen enthalten, mit denen DOS nix anfangen kann: ist letzteres der Fall, verbleibt dieser Teil des Pfads dann leider im i.d.R. langen Original. So dass der einzige mir bekannte Weg, an den 8+3-Namen auch wirklich jeder Datei zu kommen, die Option /x des DIR-Befehls ist (was zB. für mich speziell vor allem dann von Interesse ist, wenn ich eine solche, aber auch jede andere Datei mit langem Namen mit einem meiner alten DOS-Programme öffnen will, die ja nur 8+3-Namen können - da muss ich dann zunächst mit DIR /x den Kurznamen der zu öffnenden Datei freipräparieren und diesen dann in einem zweiten Schritt als Parameter an meine DOS-EXE weiterreichen).
3) Ebenfalls noch zu Pkt. "Erweiterung von Laufvariablen", muss ich zu "%~pI" auch noch was ergänzen: dieser Term extrahiert zwar definitionsgemäß den Pfad von %%I, interpretiert dabei allerdings nur alles zwischen dem ersten und letzten Backslash Befindliche als Pfad, alles ihm folgende dagegen als Dateiname - bei einer Pfadangabe der Form "C:\dir1\dir2\dir3.2" würde "%~pI" daher nur der String "\dir1\dir2\" als Pfadangabe zurückliefern, das letzte Unterverzeichnis "dir3.2" dagegen als Dateiname interpretieren und damit - als Verzeichnis! - unterschlagen, und selbst wenn man "%%~pI" nun zu "%%~pnI" erweitern wollte, würde die abschließende "2" dabei auch wieder, da nun als Extension behandelt, durchs Raster fallen. Man kann also auch hier, um wirklich den kompletten Verzeichnispfad zu erhalten, ggf. mit "%%~pnxI" arbeiten müssen.
Erinnert mich im übrigen alles so'n bissel an die alten Zeiten, als man die richtig kleinen und richtig schnellen Programme noch gleich in Assembler schrieb und einen da praktisch noch weniger vor dem Abgrund schützte als es die heutige CMD.EXE tut - da kommt man (wenigstens meist) ohne einen Neustart aus.
Man liest sich,
Qniemiec
1) In Friemlers tollen Tutorial (Hut ab!) lesen wir unter Pkt.5: "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, [...] 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." Oder eben _nicht_ mit dem Notepad, weil das nur ANSI oder UTF-8/UTF-16 kann! Habe also, als ich irgendwann auf Windows umsteigen musste, anfangs auch lange nach so etwas gesucht, also einem im Idealfall zu Windows' eigenen Bordmitteln gehörenden Editor, der sowohl ANSI als auch ASCII kann, und das ist eben nicht das Windows-Notepad, sondern das Windows-Wordpad! Das kann nämlich ANSI, UTF-16, aber auch ASCII und, wenn man 'n bissel was formatieren möchte, RTF (und das nicht nur schreiben, sondern auch lesen, s.u.)!
Also quasi das ideale Bindeglied zwischen DOS- und Windows-Texten. So dass, wenn ich seitdem irgendwelche Batchdateien schreibe, ich das zunächst einmal immer mit dem Wordpad im RTF-Format tue (und dabei, um irgendwas im Quelltext hervorzuheben, auch schön mit Farben, Fett, Kursiv und Unterstreichen arbeiten kann), und nachdem ich das dann im RTF-Format abgespeichert hab, speichere ich's mit "Speichern unter" jedesmal auch nochmal mit der Option "Textdokument - MS-DOS-Format" als ausführbare Batch mit der von mir dabei vorzugebenden Extension BAT oder CMD (weil das Wordpad selber natürlich immer erstmal TXT vorschlägt). Ist für mich, seitdem ich draufgekommen bin, der Königsweg, umso mehr, als ich dadurch auch immer zwei Versionen meiner Batch habe, eine reich illustrierte Dokumentation in Form der RTF, und dann nochmal die ausführbare Datei als BAT oder CMD.
2) Zu dem Pkt. "Erweiterung von Laufvariablen" muss ich, was "%~sI" angeht, leider was korrigieren: Diese Erweiterung funktioniert so wie beschrieben leider nur, wenn alle im Suchpfad vorkommenden Verzeichnis- und Dateinamen DOS-konform sind, also zB. keine der neuerdings so beliebten (langen) Gedankenstriche oder andere Zeichen enthalten, mit denen DOS nix anfangen kann: ist letzteres der Fall, verbleibt dieser Teil des Pfads dann leider im i.d.R. langen Original. So dass der einzige mir bekannte Weg, an den 8+3-Namen auch wirklich jeder Datei zu kommen, die Option /x des DIR-Befehls ist (was zB. für mich speziell vor allem dann von Interesse ist, wenn ich eine solche, aber auch jede andere Datei mit langem Namen mit einem meiner alten DOS-Programme öffnen will, die ja nur 8+3-Namen können - da muss ich dann zunächst mit DIR /x den Kurznamen der zu öffnenden Datei freipräparieren und diesen dann in einem zweiten Schritt als Parameter an meine DOS-EXE weiterreichen).
3) Ebenfalls noch zu Pkt. "Erweiterung von Laufvariablen", muss ich zu "%~pI" auch noch was ergänzen: dieser Term extrahiert zwar definitionsgemäß den Pfad von %%I, interpretiert dabei allerdings nur alles zwischen dem ersten und letzten Backslash Befindliche als Pfad, alles ihm folgende dagegen als Dateiname - bei einer Pfadangabe der Form "C:\dir1\dir2\dir3.2" würde "%~pI" daher nur der String "\dir1\dir2\" als Pfadangabe zurückliefern, das letzte Unterverzeichnis "dir3.2" dagegen als Dateiname interpretieren und damit - als Verzeichnis! - unterschlagen, und selbst wenn man "%%~pI" nun zu "%%~pnI" erweitern wollte, würde die abschließende "2" dabei auch wieder, da nun als Extension behandelt, durchs Raster fallen. Man kann also auch hier, um wirklich den kompletten Verzeichnispfad zu erhalten, ggf. mit "%%~pnxI" arbeiten müssen.
Erinnert mich im übrigen alles so'n bissel an die alten Zeiten, als man die richtig kleinen und richtig schnellen Programme noch gleich in Assembler schrieb und einen da praktisch noch weniger vor dem Abgrund schützte als es die heutige CMD.EXE tut - da kommt man (wenigstens meist) ohne einen Neustart aus.
Man liest sich,
Qniemiec
Hallo @Qniemiec,
deine Ergänzung ist willkommen - klar und verständlich beschrieben, was dich so auf dem Herzen bewegt.
Für viele hier in Forum ist das Windows eigene Notepad (bzw. Windows Editor) bloß eine "neutrale" Benennung, um nicht irgendwelche favorisierte Textbearbeitungsprogramme zu erwähnen, weil diese Möglichkeiten mittlerweile ("wie der Sand am Meer") unzählig sind. Mensch neigt sich bei Empfehlungen zu distanzieren - die sogenannte "Beratungsresistenz" ist stark gewachsen und gewöhnlich alles stößt erstmals auf Skepsis. Viele sind immuner geworden gegen alles, was nach Werbung klingen könnte. Nütz vom Unnütz ist ohne die eigene Erfahrung kaum zu überblicken.
Sehr viele nutzen aus einfachen und praktischen Gründen Notepad++. Ich selbst bin - wie auch viele Anwender weltweit - damit zufrieden, wegen der einfachen Handhabung und des leichten Gewichts (auch sind mir keine Ärger wie essentielle technische Probleme, Werbung und Wartungskosten bekannt). Im Notepad++ Support-Forum habe ich bisher nur einen Verbesserungsvorschlag abgegeben und seit etwa 3 Monaten bis heute immer noch unbeantwortet geblieben. Das war es aber schon meinerseits zum Meckern über das Programm.
Kurz zum WordPad
Früher, zw. 2012 - 2013, habe ich paar Monate genau so ein Szenario mit Windows WordPad gehandhabt, seit ich jedoch Notepad++ benutze, will aufgrund viele Umständen/Nachteilen einfach so keinesfalls zurück (vielleicht bloß ab und zur Not, falls mal auf irgendeinem System gerade doch kein Notepad++ vorhanden ist und weder meine Werkzeuge als Datensammlung noch Internet verfügbar sind oder verfügbar sein dürfen).
Manche Kommentare dürfen auch in Batch-Skripten hinterlegt werden und in Notepad++ gelingt dies automatisch farblich (je nachdem wie die Farben für die eigene Batch-Skriptsprache konfiguriert werden).
Aber falls du immer noch mit WordPad sehr überzeug und weiterhin begeistert bist, will ich dir die Freude nicht entnehmen und anbei paar Tipps aus 2012 noch:
"Batch-Dateien standardmäßig mit WordPad bearbeiten.reg"
"Batch-Dateien standardmäßig mit WordPad bearbeiten_Zurücksetzen.reg"
"Optionen von WordPad setzen.reg"
deine Ergänzung ist willkommen - klar und verständlich beschrieben, was dich so auf dem Herzen bewegt.
Für viele hier in Forum ist das Windows eigene Notepad (bzw. Windows Editor) bloß eine "neutrale" Benennung, um nicht irgendwelche favorisierte Textbearbeitungsprogramme zu erwähnen, weil diese Möglichkeiten mittlerweile ("wie der Sand am Meer") unzählig sind. Mensch neigt sich bei Empfehlungen zu distanzieren - die sogenannte "Beratungsresistenz" ist stark gewachsen und gewöhnlich alles stößt erstmals auf Skepsis. Viele sind immuner geworden gegen alles, was nach Werbung klingen könnte. Nütz vom Unnütz ist ohne die eigene Erfahrung kaum zu überblicken.
Sehr viele nutzen aus einfachen und praktischen Gründen Notepad++. Ich selbst bin - wie auch viele Anwender weltweit - damit zufrieden, wegen der einfachen Handhabung und des leichten Gewichts (auch sind mir keine Ärger wie essentielle technische Probleme, Werbung und Wartungskosten bekannt). Im Notepad++ Support-Forum habe ich bisher nur einen Verbesserungsvorschlag abgegeben und seit etwa 3 Monaten bis heute immer noch unbeantwortet geblieben. Das war es aber schon meinerseits zum Meckern über das Programm.
Kurz zum WordPad
"Ist für mich, seitdem ich draufgekommen bin, der Königsweg, umso mehr, als ich dadurch auch immer zwei Versionen meiner Batch habe, eine reich illustrierte Dokumentation in Form der RTF, und dann nochmal die ausführbare Datei als BAT oder CMD."
Quasi "ala Compiler" auf einfacher Art - mit Notizen, Formatierungen, Anhängen, Hyperlinks … .Früher, zw. 2012 - 2013, habe ich paar Monate genau so ein Szenario mit Windows WordPad gehandhabt, seit ich jedoch Notepad++ benutze, will aufgrund viele Umständen/Nachteilen einfach so keinesfalls zurück (vielleicht bloß ab und zur Not, falls mal auf irgendeinem System gerade doch kein Notepad++ vorhanden ist und weder meine Werkzeuge als Datensammlung noch Internet verfügbar sind oder verfügbar sein dürfen).
Manche Kommentare dürfen auch in Batch-Skripten hinterlegt werden und in Notepad++ gelingt dies automatisch farblich (je nachdem wie die Farben für die eigene Batch-Skriptsprache konfiguriert werden).
Aber falls du immer noch mit WordPad sehr überzeug und weiterhin begeistert bist, will ich dir die Freude nicht entnehmen und anbei paar Tipps aus 2012 noch:
"Batch-Dateien standardmäßig mit WordPad bearbeiten.reg"
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\batfile\shell\edit]
[HKEY_CLASSES_ROOT\batfile\shell\edit\command]
;"%ProgramFiles%\Windows NT\Accessories\wordpad.exe" "%1"
@=hex(2):22,00,25,00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,46,00,69,00,6c,\
00,65,00,73,00,25,00,5c,00,57,00,69,00,6e,00,64,00,6f,00,77,00,73,00,20,00,\
4e,00,54,00,5c,00,41,00,63,00,63,00,65,00,73,00,73,00,6f,00,72,00,69,00,65,\
00,73,00,5c,00,77,00,6f,00,72,00,64,00,70,00,61,00,64,00,2e,00,65,00,78,00,\
65,00,22,00,20,00,22,00,25,00,31,00,22,00,00,00
[HKEY_CLASSES_ROOT\batfile\shell\Notepad]
[HKEY_CLASSES_ROOT\batfile\shell\Notepad\command]
;%SystemRoot%\System32\NOTEPAD.EXE %1
@=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,4e,00,4f,00,\
54,00,45,00,50,00,41,00,44,00,2e,00,45,00,58,00,45,00,20,00,25,00,31,00,00,\
00
"Batch-Dateien standardmäßig mit WordPad bearbeiten_Zurücksetzen.reg"
Windows Registry Editor Version 5.00
;Anwendung festlegen, mit welcher die .bat-Dateien standardmäßig bearbeitet werden sollen:
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[HKEY_CLASSES_ROOT\batfile\shell\edit\command]
;%SystemRoot%\System32\NOTEPAD.EXE %1
@=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,4e,00,4f,00,\
54,00,45,00,50,00,41,00,44,00,2e,00,45,00,58,00,45,00,20,00,25,00,31,00,00,\
00
;Zusätzliche Anwendung als Alternative festlegen bzw. entfernen
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[-HKEY_CLASSES_ROOT\batfile\shell\Notepad]
[-HKEY_CLASSES_ROOT\batfile\shell\Notepad\command]
;%SystemRoot%\System32\NOTEPAD.EXE %1
;@=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,4e,00,4f,00,\
54,00,45,00,50,00,41,00,44,00,2e,00,45,00,58,00,45,00,20,00,25,00,31,00,00,\
00
"Optionen von WordPad setzen.reg"
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Applets\Wordpad\Options]
;Statusleiste anzeigen
"ShowStatusBar"=dword:00000000
;Lineal anzeigen
"ShowRuler"=dword:00000000
"Units"=dword:00000001
"Maximized"=dword:00000000
;Am Fenster umbrechen
;Noch unbekannte Einstellung: "FrameRect"=hex:50,01,00,00,80,00,00,00,bb,05,00,00,e9,03,00,00
"Wrap"=dword:00000001
"PageMargin"=hex:08,07,00,00,a0,05,00,00,08,07,00,00,a0,05,00,00
"PrintPageNum"=dword:00000001
"DefaultFormat"=dword:00000005
Hallo Evinben, danke erstmal für Dein Reply ink. der Registry-Patches, und klar, da gibt's natürlich endlos viele Alternativen: mein "Spleen" war es einfach zu schauen, ob es auch "mit ohne alles", wie der Berliner sagt, geht, also ohne irgendwelche weiteren Programme. Denn wenn's mal was Komplizierteres ist, zB. irgendwelches Sortieren oder spaltenweises Ersetzen usw., nehme ich dann natürlich auch Zuflucht zu Extraprogrammen wie Office & Co., meine Frage dagegen war, wie weit man kommt, wenn man wirklich nur das nackige Windows hat .
Man liest sich,
Qniemiec
Man liest sich,
Qniemiec
Hallo zusammen,
Beispiel 6
warum funktioniert der Befehl nicht:
for /d %%v in (Z:\*) do forfiles /p %Verz% /S /M *.* /D -30 /C "cmd /c del @File"
Davor setze ich die Variable für %Verz% mit:
set Verz=\Daten\Lokal\
Das Verzeichnis existiert auf Z:\ und ist ein Unterverzeichnis in jedem Mitarbeiterverzeichnis
Beispiel 6
warum funktioniert der Befehl nicht:
for /d %%v in (Z:\*) do forfiles /p %Verz% /S /M *.* /D -30 /C "cmd /c del @File"
Davor setze ich die Variable für %Verz% mit:
set Verz=\Daten\Lokal\
Das Verzeichnis existiert auf Z:\ und ist ein Unterverzeichnis in jedem Mitarbeiterverzeichnis
Unter Windows 11 Home wird die FOR-Schleife nicht durchlaufen, wenn eine SKIP-Angabe größer als die Anzahl der Datensätze in einer line sequential Datei.
Kann das bitte jemand bestätigen?
Hintergrund ist, dass ich versuche mit einer FOR-Schleife festzustellen, ob eine Datei in-use ist. Wenn die Datei nicht in-use ist, so wird natürlich die ganze Datei eingelesen. Dies wollte ich mit dem Skip vermeiden.
Kann das bitte jemand bestätigen?
Hintergrund ist, dass ich versuche mit einer FOR-Schleife festzustellen, ob eine Datei in-use ist. Wenn die Datei nicht in-use ist, so wird natürlich die ganze Datei eingelesen. Dies wollte ich mit dem Skip vermeiden.