FOR F mit einer variabelen Anzahl Tokens
Die
Ich möchte im folgenden ein Verfahren (zwei Unterprogramme für Batchscripts) vorstellen, mit der sich eine variable Anzahl Tokens (1 bis 31) für die
Die Syntax der
Für ausführliche Informationen verweise ich auf mein Tutorial zur FOR-Schleife.
Also müsste man
Aufgabe 1 wird vom Unterprogramm
Um den praktischen Nutzen zu erhöhen, legt
Durch einen Parameter von
Die Eingabedaten können aufgrund von Beschränkungen der
In Zeile 17 wird die Zeichenkette von allen Zeichen befreit, die dem angegebenen Trennzeichen entsprechen. Wenn diese neue Zeichenkette und die ursprüngliche danach gleich sind, wird die Verarbeitung abgebrochen.
Ansonsten wird in Zeile 20 der Zähler für die Tokens erhöht und in Zeile 22 die übergebene Zeichenkette um das erste Trennzeichen und alle Zeichen davor verkürzt. Danach wird wieder an den Schleifenanfang gesprungen.
Der Bezeichner für die erste Laufvariable wird der Variablen
Aufgabe ist, die Dateien, die sich in den Verzeichnissen
Normalerweise müsste das Batchfile zur Erstellung der ZIP-Archive auf jeden Rechner angepasst werden. Durch dynamisch erzeugte Laufvariablen/Tokens kann das Verzeichnis
Die Variable
Damit der Inhalt der Zellen auch Leerzeichen enthalten kann, wird hier der Parameter
Der Inhalt einer Tabellenzeile steht für jeden Durchlauf der
Gruß
Friemler
FOR /F
-Schleife bietet ja bekanntlich die Möglichkeit, Eingabedaten in sog. Tokens zu zerlegen. Dazu muss die Anzahl der Tokens aber schon beim erstellen des Batchscripts bekannt sein. Was aber, wenn das nicht der Fall ist? Oder man ein universell verwendbares Script schreiben möchte, das flexibel bzgl. der Anzahl der Tokens ist?Ich möchte im folgenden ein Verfahren (zwei Unterprogramme für Batchscripts) vorstellen, mit der sich eine variable Anzahl Tokens (1 bis 31) für die
FOR /F
-Schleife realisieren lässt. Anschließend folgen noch zwei Anwendungsbeispiele.Inhaltsverzeichnis
Allgemeines
Was sind Tokens?
DieFOR /F
-Schleife ist zur Verarbeitung einfach strukturierter Daten gedacht. Die einzelnen Datenfelder müssen durch Trennzeichen (Delimiter) voneinander separiert sein. Das kann z.B. der Backslash in Dateipfaden oder das Semikolon in CSV-Dateien von Excel sein. Die Datenfelder zwischen diesen Trennzeichen werden als Tokens bezeichnet. Die Standard-Trennzeichen von FOR /F
sind das Leer- und das Tabulatorzeichen. Es können aber auch (mehrere) eigene Trennzeichen definiert werden.Nutzen des Verfahrens
Man kann ein Script so entwickeln, dass es für gleichartige Eingabedaten mit einer unterschiedlichen Anzahl Tokens ohne Anpassung verwendbar ist.Die Syntax der FOR /F
-Schleife
Für ausführliche Informationen verweise ich auf mein Tutorial zur FOR-Schleife.Das Verfahren
Die Idee zu dem Verfahren kam mir, als mir auffiel, dass normale Umgebungsvariablen, deren Inhalt aus Bezeichnern von Laufvariablen derFOR /F
-Schleife besteht, innerhalb einer Schleife sozusagen doppelt ausgewertet werden. Vor Beginn der Schleife werden sie erweitert (der Code enthält nun die Bezeichner der Laufvariablen) und während der Abarbeitung der Schleife werden dann die Laufvariablen erweitert.Also müsste man
- eine Probe der Eingabedaten untersuchen, um herauszufinden, wie viele Tokens die Eingabedaten enthalten und
- eine Umgebungsvariable mit so vielen Bezeichnern für Laufvariablen füllen wie Tokens vorhanden sind.
Aufgabe 1 wird vom Unterprogramm
CountTokens
erledigt, Aufgabe 2 vom Unterprogramm GenVars
.Um den praktischen Nutzen zu erhöhen, legt
GenVars
noch eine weitere Variable an, die den Bezeichner der letzten auftretenden Laufvariablen enthält, die bei der Abarbeitung der FOR /F
-Schleife dem letzten Token entspricht.Durch einen Parameter von
GenVars
kann außerdem gesteuert werden, ob die Bezeichner der Laufvariablen in Anführungszeichen eingeschlossen werden sollen. Das ist dann von Bedeutung, wenn einzelne Tokens auch Leerzeichen enthalten könnten und so wie im Anwendungsbeispiel 2 nach der Erfassung einzeln weiterverarbeitet werden sollen.Einschränkungen
Natürlich gelten auch hier wieder die Einschränkungen des Batchscript-Interpreters in Bezug auf die Verarbeitung von Zeichenketten, die bestimmte Sonderzeichen enthalten (%
, "
, ^
, usw.). Man muss sich eben sicher sein, dass diese Sonderzeichen nicht auftreten können.Die Eingabedaten können aufgrund von Beschränkungen der
FOR
-Schleife minimal 1 Token und maximal 31 Tokens enthalten.Die Unterprogramme
CountTokens
:: Parameter:
:: %1 - Eine Zeichenkette, die darauf untersucht werden soll
:: wie viele Tokens sie enthält.
:: %2 - Ein einzelnes Zeichen, dass als Trennzeichen zwischen
:: den Tokens gelten soll.
:: Rückgabe:
:: Die Variable nTokens enthält die Anzahl der gefundenen Tokens
:CountTokens
setlocal
set "str=%~1"
set nTokens=1
:CountTokensLoop
call set "newstr=%%str:%~2=%%"
if "%newstr%" neq "%str%" (
set /a nTokens+=1
call set "str=%%str:*%~2=%%"
goto :CountTokensLoop
)
endlocal & set nTokens=%nTokens%
exit /b
Ansonsten wird in Zeile 20 der Zähler für die Tokens erhöht und in Zeile 22 die übergebene Zeichenkette um das erste Trennzeichen und alle Zeichen davor verkürzt. Danach wird wieder an den Schleifenanfang gesprungen.
GenVars
:: Parameter:
:: %1 - Ein einzelnes Zeichen, dass als Trennzeichen zwischen die Bezeichner
:: der Laufvariablen eingefügt werden soll.
:: %2 - Die Anzahl der Tokens, die von CountTokens ermittelt wurde.
:: %3 - Wenn dieser Parameter den String QUOTE enthält (Groß-/Kleinschreibung
:: egal), werden die Bezeichner der Laufvariablen in Anführungszeichen
:: eingeschlossen. Weglassen kann man den Parameter NICHT!
:: %4 und folgende sind die gewünschten Bezeichner für die Laufvariablen der
:: FOR-Schleife OHNE Prozentzeichen davor.
:: Rückgabe:
:: - Die Variable Tokens enthält die Bezeichner aller Laufvariablen für die
:: FOR-Schleife
:: - Die Variable LastToken enthält den Bezeichner der letzten Laufvariablen
:: für die FOR-Schleife
:GenVars
setlocal
set "Delim=%~1"
set "nToks=%~2"
set "Quote=%~3"
if /i "%Quote%" equ "quote" (
set Tokens="%%%4"
) else (
set Tokens=%%%4
)
set /a Cnt=1
shift & shift & shift & shift
:GenVarsLoop
if %Cnt% lss %nToks% (
if /i "%Quote%" equ "quote" (
set "Tokens=%Tokens%%Delim%"%%%1""
) else (
set "Tokens=%Tokens%%Delim%%%%1"
)
shift
set /a Cnt+=1
if "%1" neq "" goto :GenVarsLoop
)
if /i "%Quote%" equ "quote" (
set "LastToken=%Tokens:~-4%"
) else (
set "LastToken=%Tokens:~-2%"
)
endlocal & set "Tokens=%Tokens%" & set "LastToken=%LastToken%"
exit /b
Tokens
in Zeile 25 bzw. Zeile 27 zugewiesen, abhängig vom Parameter %3. Die weiteren Bezeichner werden in Zeile 36 bzw. Zeile 38 hinzugefügt. Vor die Bezeichner wird immer nur ein Prozentzeichen gesetzt, das genügt in diesem Fall. Durch die SHIFT
-Befehle in Zeile 31 bzw. Zeile 41 wird der nächste gewünschte Bezeichner in die Parametervariable %1 verschoben. Der Zähler CNT
läuft nur mit, damit auch der Fall "Anzahl der Tokens ist 1" korrekt behandelt werden kann.Anwendungsbeispiel 1
Man hat folgende Verzeichnisstruktur:Kunden
Kunden\Kunde_Maier
Kunden\Kunde_Maier\Maier_Rechnungen_2009
Kunden\Kunde_Maier\Maier_Rechnungen_2010
Kunden\Kunde_Maier\Maier_Rechnungen_2011
Kunden\Kunde_Mueller
Kunden\Kunde_Mueller\Mueller_Rechnungen_2010
Kunden\Kunde_Mueller\Mueller_Rechnungen_2011
Kunden\Kunde_Schulze
Kunden\Kunde_Schulze\Schulze_Rechnungen_2008
Kunden\Kunde_Schulze\Schulze_Rechnungen_2009
Kunden\Kunde_Schulze\Schulze_Rechnungen_2010
X_Rechnungen_Y
befinden, in passwortgeschützte ZIP-Archive zu packen, die den Namen des Verzeichnisses erhalten, aus dem die enthaltenen Dateien stammen, z.B. Maier_Rechnungen_2009.zip
. Diese Verzeichnisstruktur kann auf mehreren Rechnern existieren, immer unter einem anderen Basispfad mit einer verschiedenen Anzahl übergeordneter Verzeichnisse. Das ist zwar kein Real-World-Beispiel und zeugt von einer schlechten Organisation, aber das ist hier nicht das Thema.Normalerweise müsste das Batchfile zur Erstellung der ZIP-Archive auf jeden Rechner angepasst werden. Durch dynamisch erzeugte Laufvariablen/Tokens kann das Verzeichnis
Kunden
auf der 1. bis (31-FolderDepth
). Ebene einer Verzeichnisstruktur liegen. Die Variable FolderDepth
gibt die Anzahl der Verzeichnisse unterhalb von Kunden
an. Die Variable SrcDir
muss immer einen vollständigen Pfad inkl. Laufwerk enthalten.@echo off
setlocal
set "SrcDir=E:\Test1\Test2\Kunden"
set "DestDir=E:\Rechnungen_gezippt"
set FolderDepth=2
set "Delimiter=\"
call :CountTokens "%SrcDir%" "%Delimiter%"
set /a nTokens+=FolderDepth
call :GenVars "%Delimiter%" %nTokens% noquote ? @ 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 [ \ ]
for /f "eol= delims=%Delimiter% tokens=1-%nTokens%" %%? in ('dir /b /s /a:d /o:ne "%SrcDir%" 2^>NUL') do (
if "%LastToken%" neq "" (
7z.exe a -pGEHEIM -mx0 "%DestDir%\%LastToken%.zip" "%Tokens%\*.*"
)
)
exit /b
LastToken
enthält den Bezeichner der letzten automatisch erzeugten Laufvariablen, die hier den Namen des Verzeichnisses mit den zu verarbeitenden Dateien repräsentiert. Nur wenn diese Variable einen Inhalt hat, wird gerade die richtige Verzeichnisebene betrachtet, deshalb Zeile 18.Anwendungsbeispiel 2
Der Inhalt der Zellen einer Excel-Tabelle, die als CSV-Datei vorliegt, soll für jede Zelle einzeln weiterverarbeitet werden, unabhängig davon, wie viele Spalten die Tabelle hat. Die Anzahl der Spalten darf aber höchstens 31 sein.@echo off
setlocal
set "SrcFile=E:\Test.csv"
set /p "Line=" < "%SrcFile%"
set "Delimiter=;"
call :CountTokens "%Line%" "%Delimiter%"
call :GenVars " " %nTokens% quote ? @ 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 [ \ ]
for /f "usebackq eol= delims=%Delimiter% tokens=1-%nTokens%" %%? in ("%SrcFile%") do (
set "Line=%Tokens%"
call :ProcessLine
echo.
)
endlocal
exit /b
:ProcessLine
for %%i in (%Line%) do <NUL set /p "=%%~i" & echo.
exit /b
QUOTE
benutzt. Die Bezeichner der Laufvariablen werden dadurch in Anführungszeichen gesetzt.Der Inhalt einer Tabellenzeile steht für jeden Durchlauf der
FOR
-Schleife im Hauptprogramm in der Variablen Line
zu Verfügung. Die Zelleninhalte sind durch Leerzeichen separiert und in Anführungszeichen eingefasst und können somit von der FOR
-Schleife in Zeile 24 ausgegeben werden.Gruß
Friemler
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 167111
Url: https://administrator.de/contentid/167111
Ausgedruckt am: 26.11.2024 um 23:11 Uhr
5 Kommentare
Neuester Kommentar
Hallo,
zuerst mal: Gutes tut, wie immer
Nur noch kurz eine Frage:
1. Warum ist in Beispiel 2 die Zeile 14 in der Schleife?
2.
Würde sich diese Idee nicht irgendwie so verwirklichen lassen?
MfG,
Mathe172
zuerst mal: Gutes tut, wie immer
Nur noch kurz eine Frage:
1. Warum ist in Beispiel 2 die Zeile 14 in der Schleife?
2.
Würde sich diese Idee nicht irgendwie so verwirklichen lassen?
@echo off & setlocal enabledelayedexpansion
set "Delim=:"
set "Rare="
::Oder anderes seltenens Zeichen
set "ToSplit=Das:ist:ein:Test"
set "ToSplit=!ToSplit: =%Rare%!"
set "ToSplit=!ToSplit:%Delim%= !"
for %%A in (%ToSplit%) do (
set "Token=%%A"
set "Token=!Token:%Rare%= !"
call :DoSomething !Token!
)
goto :eof
:DoSomething
echo.%~1
goto :eof
MfG,
Mathe172
Hallo,
Zu 2.: Du hast wie immer recht, es ist ein gefährlich(und aufwendig). (Das muss ja niemand wissen )
MfG,
Mathe172
Die Idee zu dem Verfahren kam mir, als mir auffiel, dass normale Umgebungsvariablen, deren Inhalt aus Bezeichnern von Laufvariablen der FOR /F-Schleife besteht, innerhalb einer Schleife sozusagen doppelt ausgewertet werden. Vor Beginn der Schleife werden sie erweitert (der Code > enthält nun die Bezeichner der Laufvariablen) und während der Abarbeitung der Schleife werden dann die Laufvariablen erweitert.
Uups, sollte besser lesen Zu 2.: Du hast wie immer recht, es ist ein gefährlich(und aufwendig). (Das muss ja niemand wissen )
MfG,
Mathe172
Hallo Friemler,
zwei Gedanken kamen mir da so.
Wieso nimmst du nicht einfach immer die Maximalanzahl an tokens an? Wenn es weniger sind ist es ja auch nicht tragisch.
Warum teilst du die einzelnen Zeilen nicht durch Linefeeds, dann fällt doch auch die Begrenzung auf maximal 31 Tokens/Spalten weg.
Also z.B. um eine CSV-Datei zu lesen
Beispiel Ausgabe:
Normalerweise kann man mit FOR /F einen String nur in einzelne Token zerlegen, mit Linefeeds kann man aber pro delim eine eigene Zeile erzeugen und daher auch beliebig viele "Spalten" bearbeiten.
Die # hänge ich vor jede Spalte damit ich auch die leeren Spalten erwische.
jeb
zwei Gedanken kamen mir da so.
Wieso nimmst du nicht einfach immer die Maximalanzahl an tokens an? Wenn es weniger sind ist es ja auch nicht tragisch.
Warum teilst du die einzelnen Zeilen nicht durch Linefeeds, dann fällt doch auch die Begrenzung auf maximal 31 Tokens/Spalten weg.
Also z.B. um eine CSV-Datei zu lesen
@echo off
setlocal DisableDelayedExpansion
set LF=^
set rowCount=0
FOR /F ^"eol^=^
delims^=^" %%R in (table.csv) do (
set /a rowCount+=1
set "row=%%R"
call :splitRow
echo(
)
goto :eof
:splitRow
setlocal EnableDelayedExpansion
for %%L in ("!LF!") do set "row=#!row:;=%%~L#!"
FOR /F ^"eol^=^
delims^=^" %%C in ("!row!") do (
set column=%%C
echo Row #!rowCount! Column = !column:~1!
)
endlocal
goto :eof
Beispiel Ausgabe:
Row #1 Column = hallo
Row #1 Column = zwei
Row #1 Column = drei
Row #2 Column = Line2
Row #2 Column = vier
Row #2 Column = fünf
Normalerweise kann man mit FOR /F einen String nur in einzelne Token zerlegen, mit Linefeeds kann man aber pro delim eine eigene Zeile erzeugen und daher auch beliebig viele "Spalten" bearbeiten.
Die # hänge ich vor jede Spalte damit ich auch die leeren Spalten erwische.
jeb