Top-Themen

AppleEntwicklungHardwareInternetLinuxMicrosoftMultimediaNetzwerkeOff TopicSicherheitSonstige SystemeVirtualisierungWeiterbildungZusammenarbeit

Aktuelle Themen

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

FOR F mit einer variabelen Anzahl Tokens

Anleitung Entwicklung Batch & Shell

Mitglied: Friemler

Friemler (Level 2) - Jetzt verbinden

28.05.2011, aktualisiert 21:28 Uhr, 21294 Aufrufe, 5 Kommentare, 3 Danke

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




Allgemeines


Was sind Tokens?

Die FOR /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 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.

Also müsste man
  1. eine Probe der Eingabedaten untersuchen, um herauszufinden, wie viele Tokens die Eingabedaten enthalten und
  2. 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

01.
:: Parameter: 
02.
:: %1 - Eine Zeichenkette, die darauf untersucht werden soll 
03.
::      wie viele Tokens sie enthält. 
04.
:: %2 - Ein einzelnes Zeichen, dass als Trennzeichen zwischen 
05.
::      den Tokens gelten soll. 
06.
 
07.
:: Rückgabe: 
08.
:: Die Variable nTokens enthält die Anzahl der gefundenen Tokens 
09.
 
10.
:CountTokens 
11.
setlocal 
12.
 
13.
set "str=%~1" 
14.
set nTokens=1 
15.
 
16.
:CountTokensLoop 
17.
call set "newstr=%%str:%~2=%%" 
18.
 
19.
if "%newstr%" neq "%str%" ( 
20.
  set /a nTokens+=1 
21.
 
22.
  call set "str=%%str:*%~2=%%" 
23.
  goto :CountTokensLoop 
24.
25.
 
26.
endlocal & set nTokens=%nTokens% 
27.
exit /b
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.


GenVars

01.
:: Parameter: 
02.
:: %1 - Ein einzelnes Zeichen, dass als Trennzeichen zwischen die Bezeichner 
03.
::      der Laufvariablen eingefügt werden soll. 
04.
:: %2 - Die Anzahl der Tokens, die von CountTokens ermittelt wurde. 
05.
:: %3 - Wenn dieser Parameter den String QUOTE enthält (Groß-/Kleinschreibung 
06.
::      egal), werden die Bezeichner der Laufvariablen in Anführungszeichen 
07.
::      eingeschlossen. Weglassen kann man den Parameter NICHT! 
08.
:: %4   und folgende sind die gewünschten Bezeichner für die Laufvariablen der 
09.
::      FOR-Schleife OHNE Prozentzeichen davor. 
10.
 
11.
:: Rückgabe: 
12.
:: - Die Variable Tokens enthält die Bezeichner aller Laufvariablen für die 
13.
::   FOR-Schleife 
14.
:: - Die Variable LastToken enthält den Bezeichner der letzten Laufvariablen 
15.
::   für die FOR-Schleife 
16.
 
17.
:GenVars 
18.
setlocal 
19.
 
20.
set "Delim=%~1" 
21.
set "nToks=%~2" 
22.
set "Quote=%~3" 
23.
 
24.
if /i "%Quote%" equ "quote" ( 
25.
  set Tokens="%%%4" 
26.
) else ( 
27.
  set Tokens=%%%4 
28.
29.
 
30.
set /a Cnt=1 
31.
shift & shift & shift & shift 
32.
 
33.
:GenVarsLoop 
34.
if %Cnt% lss %nToks% ( 
35.
  if /i "%Quote%" equ "quote" ( 
36.
    set "Tokens=%Tokens%%Delim%"%%%1"" 
37.
  ) else ( 
38.
    set "Tokens=%Tokens%%Delim%%%%1" 
39.
40.
 
41.
  shift 
42.
 
43.
  set /a Cnt+=1 
44.
  if "%1" neq "" goto :GenVarsLoop 
45.
46.
 
47.
if /i "%Quote%" equ "quote" ( 
48.
  set "LastToken=%Tokens:~-4%" 
49.
) else ( 
50.
  set "LastToken=%Tokens:~-2%" 
51.
52.
 
53.
endlocal & set "Tokens=%Tokens%" & set "LastToken=%LastToken%" 
54.
exit /b
Der Bezeichner für die erste Laufvariable wird der Variablen 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
Aufgabe ist, die Dateien, die sich in den Verzeichnissen 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.
01.
@echo off 
02.
 
03.
setlocal 
04.
 
05.
set "SrcDir=E:\Test1\Test2\Kunden" 
06.
set "DestDir=E:\Rechnungen_gezippt" 
07.
set FolderDepth=2 
08.
 
09.
set "Delimiter=\" 
10.
 
11.
 
12.
call :CountTokens "%SrcDir%" "%Delimiter%" 
13.
set /a nTokens+=FolderDepth 
14.
 
15.
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 [ \ ] 
16.
 
17.
for /f "eol= delims=%Delimiter% tokens=1-%nTokens%" %%? in ('dir /b /s /a:d /o:ne "%SrcDir%" 2^>NUL') do ( 
18.
  if "%LastToken%" neq "" ( 
19.
    7z.exe a -pGEHEIM -mx0 "%DestDir%\%LastToken%.zip" "%Tokens%\*.*" 
20.
21.
22.
 
23.
exit /b
Die Variable 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.
01.
@echo off 
02.
 
03.
setlocal 
04.
 
05.
set "SrcFile=E:\Test.csv" 
06.
set /p "Line=" < "%SrcFile%" 
07.
 
08.
set "Delimiter=;" 
09.
 
10.
call :CountTokens "%Line%" "%Delimiter%" 
11.
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 [ \ ] 
12.
 
13.
for /f "usebackq eol= delims=%Delimiter% tokens=1-%nTokens%" %%? in ("%SrcFile%") do ( 
14.
  set "Line=%Tokens%" 
15.
  call :ProcessLine 
16.
  echo. 
17.
18.
 
19.
endlocal 
20.
exit /b 
21.
 
22.
 
23.
:ProcessLine 
24.
for %%i in (%Line%) do <NUL set /p "=%%~i" & echo. 
25.
exit /b
Damit der Inhalt der Zellen auch Leerzeichen enthalten kann, wird hier der Parameter 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
Mitglied: mathe172
13.06.2011 um 21:29 Uhr
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?
01.
@echo off & setlocal enabledelayedexpansion 
02.
set "Delim=:" 
03.
set "Rare=" 
04.
::Oder anderes seltenens Zeichen 
05.
set "ToSplit=Das:ist:ein:Test" 
06.
 
07.
set "ToSplit=!ToSplit: =%Rare%!" 
08.
set "ToSplit=!ToSplit:%Delim%= !" 
09.
 
10.
for %%A in (%ToSplit%) do ( 
11.
set "Token=%%A" 
12.
set "Token=!Token:%Rare%= !" 
13.
call :DoSomething !Token! 
14.
15.
goto :eof 
16.
 
17.
:DoSomething 
18.
echo.%~1 
19.
goto :eof
MfG,
Mathe172
Bitte warten ..
Mitglied: Friemler
14.06.2011 um 13:57 Uhr
Hallo mathe,

Zu Frage 1:
Die Variable Tokens enthält doch die Bezeichner einer variablen Anzahl von Laufvariablen der FOR-Schleife. In Zeile 14 werden diese durch die Werte der zugehörigen Tokens ersetzt.

Zu Frage 2:
Sicher, in Bezug auf Beispiel 2 ließe sich Dein Code auch verwenden. Was ich daran aber nicht so gut finde ist die Ersetzung von Leerzeichen durch ein selten benutztes Zeichen. Evtl. ist genau dieses Zeichen doch mal im zu zerlegenden Text enthalten, kann man vorher ja nie wissen. Du hast hier zwar den Smily aus dem DOS-Zeichensatz verwendet, aber dieses Zeichen (und alle mit einem Code kleiner 32) kann ich im von mir favorisierten Texteditor TextPad nicht eingeben, müsste zur Änderung dieses einen Zeichens den Code extra in MS-Edit laden und ändern.

Das ganze war auch mehr zur Vorstellung eines Konzepts gedacht, sozusagen Grundlagenforschung für FOR in Batchscript. Und wie das mit Grundlagenforschung so ist: Wie man es sinnvoll einsetzt muss jetzt wieder ein anderer erforschen.

Gruß
Friemler
Bitte warten ..
Mitglied: mathe172
14.06.2011 um 16:45 Uhr
Hallo,

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
Bitte warten ..
Mitglied: jeb-the-batcher
20.06.2011 um 14:23 Uhr
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

01.
@echo off 
02.
setlocal DisableDelayedExpansion 
03.
set LF=^ 
04.
 
05.
 
06.
set rowCount=0 
07.
FOR /F ^"eol^=^ 
08.
 
09.
delims^=^" %%R in (table.csv) do ( 
10.
  set /a rowCount+=1 
11.
  set "row=%%R" 
12.
  call :splitRow  
13.
  echo( 
14.
15.
goto :eof 
16.
 
17.
:splitRow 
18.
setlocal EnableDelayedExpansion 
19.
for %%L in ("!LF!") do set "row=#!row:;=%%~L#!" 
20.
FOR /F ^"eol^=^ 
21.
 
22.
delims^=^" %%C in ("!row!") do ( 
23.
  set column=%%C 
24.
  echo Row #!rowCount! Column = !column:~1! 
25.
26.
endlocal 
27.
goto :eof
Beispiel Ausgabe:
01.
Row #1 Column = hallo 
02.
Row #1 Column = zwei 
03.
Row #1 Column = drei 
04.
 
05.
Row #2 Column = Line2 
06.
Row #2 Column = vier 
07.
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
Bitte warten ..
Mitglied: Friemler
20.06.2011 um 15:38 Uhr
Hallo jeb,

Zitat von jeb-the-batcher:
Wieso nimmst du nicht einfach immer die Maximalanzahl an tokens an? Wenn es weniger sind ist es ja auch nicht tragisch.
Beim Beispiel 1 ist es wichtig, an das letzte vorhandene Token heranzukommen.

Zitat von jeb-the-batcher:
Warum teilst du die einzelnen Zeilen nicht durch Linefeeds
Weil das Deine Spezialtricks sind.

bastla hat mir per PN schon geschrieben, dass für Beispiel 1 diese Sache auch nicht notwendig ist. Und Beispiel 2 lässt sich im konkreten Fall sicher noch auf ein paar mehr Wegen lösen als denen, die von Mathe und Dir beschrieben wurden. Wie gesagt, es sollte nur ein Konzept vorgestellt werden. Evtl. ist es ja wirklich nicht praxisrelevant, aber evtl. auch gut zu wissen, dass es geht.

Gruß
Friemler
Bitte warten ..
Neuester Wissensbeitrag
Windows 10

Powershell 5 BSOD

(8)

Tipp von agowa338 zum Thema Windows 10 ...

Ähnliche Inhalte
C und C++
gelöst Anzahl der Buchstaben in einem String Element Array C++ (3)

Frage von Protected zum Thema C und C ...

Zusammenarbeit
Minimale Anzahl Admins? (6)

Frage von 1410640014 zum Thema Zusammenarbeit ...

Batch & Shell
gelöst 1 PDF entsprechend der Anzahl duplizieren (5)

Frage von Kalisser zum Thema Batch & Shell ...

Heiß diskutierte Inhalte
Microsoft
Ordner mit LW-Buchstaben versehen und benennen (21)

Frage von Xaero1982 zum Thema Microsoft ...

Netzwerkmanagement
gelöst Anregungen, kleiner Betrieb, IT-Umgebung (18)

Frage von Unwichtig zum Thema Netzwerkmanagement ...

Windows Update
Treiberinstallation durch Windows Update läßt sich nicht verhindern (17)

Frage von liquidbase zum Thema Windows Update ...

Windows Tools
gelöst Aussendienst Datensynchronisierung (12)

Frage von lighningcrow zum Thema Windows Tools ...