Kommentiertes Batch-Beispiel - Auswertung von Logdateien nach Zeiten
Hallo @all!
Die folgende "Auswertung von Logdateien" soll die Verwendung einiger oft benötigter Batch-Elemente zeigen:
Ausgangssituation (siehe dazu den Originalthread unter Suchen von Strings mit von Batch files ):
Zwei Logdateien ("D:\Zeiten.txt", "D:\Gesamtlog.txt") enthalten Zeitangaben bzw zugeordnete Ereignisse.
Aufbau der Datei "Zeiten":
Inhalt von "Gesamtlog":
Zu jeder in "Zeiten" enthaltenen Uhrzeit können mehrere Zeilen (mit der Zeit und zusätzlichen Informationen) vorkommen, zB:
Ziel ist eine Auswertung, welche für jede in "Zeiten" erfasste Uhrzeit die Anzahl der zugehörigen Einträge aus der Datei "Ereignisse" ermittelt - das Ergebnis für den oben dargestellten Auszug aus dem "Gesamtlog" wäre etwa:
Diese Ausgabe lässt sich mit folgendem Batch erreichen:
Zur Funktion der einzelnen Zeilen:
Während "@echo off" (zum Unterdrücken der Vorweg-Anzeige des auszuführenden Befehles) und "setlocal" (um die Gültigkeit von innerhalb des Batches neu erstellten Variablen auf diesen Batch zu beschränken) eigentlich Standard für den Beginn eines Batches sind, muss hier auch "enabledelayedexpansion" hinzugefügt werden - mehr dazu unten.
Auch wenn im weiteren Ablauf die beiden Dateien nur noch je einmal benötigt werden, sorgt die Verwendung von Variablen dennoch für eine übersichtlichere Darstellung und leichtere Wartbarkeit.
Schleife, um alle Zeilen der Zeiten-Tabelle einzeln in der Variable %%i zu erhalten.
Rücksetzen des Zählers für Fundstellen (das "/a" ist eigentlich nicht nötig - zeigt aber sozusagen den "Datentyp" der Variablen - nämlich "numerisch" - an).
Schleife über alle Zeilen, welche %%i (die vorhin ausgelesene Uhrzeit) enthalten
Zwischenspeichern der ermittelten Anzahl und "Auffüllen" von links her mit "_"
Ausgabe der Uhrzeit und der (formatierten) Anzahl der Fundstellen
Um auch die letzte Zeile nicht zu unterschlagen:
Damit wird die äußere "for"-Schleife abgeschlossen (ansonsten wäre der "do"-Teil auf den Rest der Zeile 5 beschränkt gewesen).
Anzumerken wäre, dass die vorgestellte Losung ev noch Optimierungspotenzial bietet - so könnte etwa bei Verwendung von "find" mit der Option "/c" (anstelle von "findstr") das Zählen durch den Befehl selbst erfolgen - allerdings ginge dies zulasten der Flexibilität, da die Ausgabe von "find" (am Beispiel der Zeit "00:05:00") folgendes Format hat:
Weder die Leerzeichen, noch uU der ":" (der Pfad könnte - theoretisch - auch relativ bzw ohne Laufwerksangabe festgelegt sein) bieten ein sicheres Trennzeichen für eine Zerlegung - leicht zu erkennen, wenn Pfad und Dateiname zB auf "..\Aktuelle Logs\Gesamtlog Ereignisse.txt" geändert würden und daher das "find"-Ergebnis so aussähe:
In diesem Fall müsste zusätzlich auch der Batchcode angepasst werden. [Edit] Mit der von LotPings unten dargestellten Schreibweise (per Eingabumleitung) wird auch "find /c" uneingeschränkt verwendbar. [/Edit]
Um trotzdem auch diese Variante darzustellen: Die Zeile 6 könnte entfallen, und die Zeile 7 (die neue Zeile 6) wäre:
Voraussetzung ist hier, dass der Pfad/Dateiname keine Leerzeichen enthält, da ansonsten die Anzahl nicht im (anhand des Default-Trennzeichens "Leerzeichen" zerlegten) Teil ("token") 3 der Zeile zu finden wäre - für das zweite Beispiel oben wäre etwa "tokens=5" erforderlich.
Die Ausgabe des Batches erfolgt auf den Bildschirm - um das Ergebnis in Dateiform zu erhalten, könnte entweder der Batch (Annahme: gespeichert im aktuellen Verzeichnis unter "Auswertung.cmd") mit Ausgabeumleitung gestartet
oder die Umleitung in der letzten Zeile ergänzt werden:
Eine vorläufig endgültige Fassung sähe dann so aus:
Es würde mich freuen, wenn dieses Beispiel hilfreich war - für Fragen, Anregungen oder Beschwerden habe ich aber gleich unterhalb jede Menge Platz gelassen ...
Grüße
bastla
Die folgende "Auswertung von Logdateien" soll die Verwendung einiger oft benötigter Batch-Elemente zeigen:
- "for /f"-Schleifen zum zeilenweisen Auslesen einer Datei bzw der Ausgabe eines Befehles
- Suche in einer Datei mittels "findstr"
- Verwendung einer Variablen als Zähler
- Auslesen des innerhalb einer Schleife veränderten Variableninhaltes (Stichwort "verzögerte Variablenauflösung" bzw "delayedExpansion")
- Formatierung einer Ausgabe (Zahl rechtsbündig)
- Ausgabe auf Bildschirm oder in eine Datei
Ausgangssituation (siehe dazu den Originalthread unter Suchen von Strings mit von Batch files ):
Zwei Logdateien ("D:\Zeiten.txt", "D:\Gesamtlog.txt") enthalten Zeitangaben bzw zugeordnete Ereignisse.
Aufbau der Datei "Zeiten":
00:00:00
00:01:00
00:02:00
00:03:00
00:04:00
00:05:00
00:06:00
........
23:59:00
Zu jeder in "Zeiten" enthaltenen Uhrzeit können mehrere Zeilen (mit der Zeit und zusätzlichen Informationen) vorkommen, zB:
00:02:00 Ereigniskennung: 68
00:02:00 Ort: K-1-08
00:02:00 Zusatz: -
00:05:00 Ereigniskennung: 07
00:05:00 Ort: G-3-02
00:05:00 Zusatz: Hier könnten zusätzliche Informationen,
00:05:00 Zusatz: auch auf mehrere Zeilen verteilt,
00:05:00 Zusatz: protokolliert werden - den Text der
00:05:00 Zusatz: übrigen 8 Zeilen spare ich ein ;-)
00:05:00 Zusatz:
00:05:00 Zusatz:
00:05:00 Zusatz:
00:05:00 Zusatz:
00:05:00 Zusatz:
00:05:00 Zusatz:
00:05:00 Zusatz:
00:05:00 Zusatz:
usw
Ziel ist eine Auswertung, welche für jede in "Zeiten" erfasste Uhrzeit die Anzahl der zugehörigen Einträge aus der Datei "Ereignisse" ermittelt - das Ergebnis für den oben dargestellten Auszug aus dem "Gesamtlog" wäre etwa:
00:00:00___0
00:01:00___0
00:02:00___3
00:03:00___0
00:04:00___0
00:05:00__14
00:06:00___0
........___0
23:59:00___0
Diese Ausgabe lässt sich mit folgendem Batch erreichen:
@echo off & setlocal enabledelayedexpansion
set "Zeiten=D:\Zeiten.txt"
set "Log=D:\Gesamtlog.txt"
for /f "usebackq delims=" %%i in ("%Zeiten%") do (
set /a Anzahl=0
for /f %%a in ('findstr /c:"%%i" "%Log%"') do set /a Anzahl+=1
set "Formatiert=____!Anzahl!"
echo %%i!Formatiert:~-4!
)
Zur Funktion der einzelnen Zeilen:
@echo off & setlocal enabledelayedexpansion
set "Zeiten=D:\Zeiten.txt"
set "Log=D:\Gesamtlog.txt"
for /f "usebackq delims=" %%i in ("%Zeiten%") do (
- "usebackq" erlaubt es, den Dateinamen/-pfad (in der Variablen %Zeiten%) unter Anführungszeichen angeben zu können - nur für den Fall, dass dieser (etwa nach einer Änderung) Leerzeichen enthielte.
- "delims=" legt fest, dass es keine Trennzeichen (zur Aufteilung der Zeile in einzelne "tokens") gibt - daher wird %%i jeweils die gesamte Zeile enthalten.
- Die Verwendung der Klammer ermöglicht, mehrere Batchzeilen für jede Zeile der ausgelesenen Datei "Zeiten" auszuführen und damit den "do"-Teil zu erweitern.
set /a Anzahl=0
for /f %%a in ('findstr /c:"%%i" "%Log%"') do set /a Anzahl+=1
- "findstr" mit dem Suchbegriff "Uhrzeit" (%%i) sorgt dafür, dass nur "passende" Zeilen aus der "Gesamtlog"-Datei herausgefiltert und in der Schleife verwendet werden.
- Auch hier sollen die verwendeten Anführungszeichen um die Variable %Log% herum für Sicherheit sorgen (Leerzeichen im Dateinamen/-pfad).
- Da die Daten selbst nicht weiter interessieren (sondern nur das Vorhandensein der Zeile), muss bei der "for"-Schleife weder für "tokens" noch für "delims" eine Einstellung vorgenommen werden.
- Die Schleife wird für jede gefundene Zeile durchlaufen - daher kann mit "set /a Anzahl+=1" (als Kurzfassung für "set /a Anzahl=%Anzahl%+1", oder hier eigentlich "set /a Anzahl=!Anzahl!+1" - warum, steht gleich im nächsten Absatz ) jeweils der "Zählerstand" in der Variablen %Anzahl% (bzw !Anzahl!) erhöht werden. Hier ist "/a" wichtig, damit eine arithmetische Operation vorgenommen wird.
set "Formatiert=____!Anzahl!"
- Damit die Ausgabe übersichtlicher wird, sollen die einzelnen Anzahlen rechtsbündig ausgerichtet untereinander stehen - die Verwendung von "___" erleichtert es, den Zusammenhang zwischen Uhrzeit und Anzahl in der Ausgabe zu erkennen.
- Damit bereits hier - innerhalb der Schleife - der jeweils aktuellen Wert der Variablen %Anzahl% gelesen (und dann auch ausgeben) werden kann, wird die oben festgelegte "delayedExpansion" benötigt - anderenfalls wäre das Ergebnis immer gleich: ein leerer String. Begründung: Bei nicht "verzögerter" Variablenauflösung würde die Ermittlung des Inhaltes von %Anzahl% nur einmal, und zwar zu Beginn der (ersten) Schleife erfolgen - zu diesem Zeitpunkt gibt es aber die Variable noch gar nicht, und daher würde noch nicht einmal "0" als Ergebnis aufscheinen. "Verzögerung" bedeutet, dass mit der Auswertung der Variablen gewartet wird, bis diese tatsächlich benötigt wird - so kann der aktuelle Wert auch innerhalb der Schleife ermittelt werden. Gekennzeichnet wird die veränderte Behandlung der Variablen durch die Verwendung von "!" anstelle von "%", daher also die Schreibweise !Anzahl!.
echo %%i!Formatiert:~-4!
- Um die Ausgabe der Anzahl und der davor befindlichen "_" immer auf insgesamt 4 Zeichen festzulegen, wird ein Teilstring gebildet: ":~-4" zählt die Zeichen vom Ende "nach vorne" (daher das negative Vorzeichen), sodass also immer die gesamte Zahl (wenn diese nicht mehr als 4 Stellen hat ) und eine passende Anzahl von "_" (3 bei einstelligen Anzahlen, 2 bei zweistelligen, ...) im Teilstring enthalten ist.
- Auch hier ist wieder "delayedExpansion" erforderlich, da auch die Variable %Formatiert% ihren Inhalt erst in der Schleife erhält und daher nur als !Formatiert! ausgelesen werden kann.
Um auch die letzte Zeile nicht zu unterschlagen:
)
Anzumerken wäre, dass die vorgestellte Losung ev noch Optimierungspotenzial bietet - so könnte etwa bei Verwendung von "find" mit der Option "/c" (anstelle von "findstr") das Zählen durch den Befehl selbst erfolgen - allerdings ginge dies zulasten der Flexibilität, da die Ausgabe von "find" (am Beispiel der Zeit "00:05:00") folgendes Format hat:
---------- D:\GESAMTLOG.TXT: 14
---------- ..\AKTUELLE LOGS\GESAMTLOG EREIGNISSE.TXT: 14
Um trotzdem auch diese Variante darzustellen: Die Zeile 6 könnte entfallen, und die Zeile 7 (die neue Zeile 6) wäre:
for /f "tokens=3" %%a in ('find /c "%%i" "%Log%"') do set "Anzahl=%%a"
Die Ausgabe des Batches erfolgt auf den Bildschirm - um das Ergebnis in Dateiform zu erhalten, könnte entweder der Batch (Annahme: gespeichert im aktuellen Verzeichnis unter "Auswertung.cmd") mit Ausgabeumleitung gestartet
Auswertung.cmd>D:\Anzahlen.txt
)>>"D:\Anzahlen.txt"
- Im ersten Fall wird die "Gesamtausgabe" des Batches in eine Datei umgeleitet - dadurch kann mit der Schreibweise ">" automatisch eine bereits vorhandene Ausgabedatei durch die neue Ausgabe überschrieben werden.
- Bei der Umleitung innerhalb des Batches muss hingegen mit ">>" ein Hinzufügen der einzelnen Zeilen zum jeweiligen "Zwischenergebnis" erfolgen (">" würde dazu führen, dass nur die letzte Zeile in der Datei stünde) - dies macht das Löschen einer ev schon bestehenden Ergebnisdatei am Beginn des Batches erforderlich.
- Natürlich sollte auch der Dateipfad der Ausgabedatei besser in eine Variable geschrieben werden.
Eine vorläufig endgültige Fassung sähe dann so aus:
@echo off & setlocal enabledelayedexpansion
set "Zeiten=D:\Zeiten.txt"
set "Log=D:\Gesamtlog.txt"
set "Erg=D:\Anzahlen.txt"
if exist "%Erg%" del "%Erg%"
for /f "usebackq delims=" %%i in ("%Zeiten%") do (
set /a Anzahl=0
for /f %%a in ('findstr /c:"%%i" "%Log%"') do set /a Anzahl+=1
set "Formatiert=____!Anzahl!"
echo %%i!Formatiert:~-4!
)>>"%Erg%"
Es würde mich freuen, wenn dieses Beispiel hilfreich war - für Fragen, Anregungen oder Beschwerden habe ich aber gleich unterhalb jede Menge Platz gelassen ...
Grüße
bastla
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 94181
Url: https://administrator.de/contentid/94181
Ausgedruckt am: 24.11.2024 um 16:11 Uhr
6 Kommentare
Neuester Kommentar
Ein sehr nützliches Tutorial, auch wenn man die meisten beschriebenen Dinge als "Batch"-Programmierer wissen sollte . Du hast alles auf den Punkt gebracht was wichtig ist ohne zuweit ausholen zu müssen. Perfekter kann man ein Tutorial nicht schreiben.
Wie auch all deine Kommentare sehr vorbildlich.... weiter so
Grüße vom AndreasA aus Bärlin
Hallo bastla,
Ich finde dein Tutorial auch gut.
Kleiner Verschlimmbesserungsvorschlag:
Der Find Befehl hat die /C Option zum nur Zählen, ohne die einzelnen Fundstellen auszugeben,
damit dürfte der Batch bei großen Logdateien schneller sein. Das Rücksetzen von Anzahl entfällt dann auch.
Technisch gesehen ist der For Befehl mit den beiden nächsten eingerückten Zeilen ein einziger Befehl.
Das ^ als letztes Zeichen Escaped das Return und der For Befehl kann hinter der öffnenden bzw. vor
der schließenden Klammer einfach in der nächsten Zeile fortgesetzt werden da der Cmd-Interpreter
weitere Information voraussetzt. Diese Schreibweise erleichtert das posten speziel in newsgroups mit
max ~72 Zeichen/Zeile
Setzt die Dateilänge auf Null, unabhängig von der vorherigen Existenz der Datei.
Zum Erzeugen der Zeilen-Datei im Minuten-Abstand:
Gruß
Ich finde dein Tutorial auch gut.
Kleiner Verschlimmbesserungsvorschlag:
Der Find Befehl hat die /C Option zum nur Zählen, ohne die einzelnen Fundstellen auszugeben,
damit dürfte der Batch bei großen Logdateien schneller sein. Das Rücksetzen von Anzahl entfällt dann auch.
:: LogZeiten
@echo off & setlocal enabledelayedexpansion
set "Zeiten=.\Zeiten.txt"
set "Log=.\Gesamt.log"
set "Erg=.\Anzahlen.txt"
Type NUL >"%Erg%"
for /f "usebackq delims=" %%i in ("%Zeiten%"
) do for /f %%a in ('find /c "%%i" ^<"%Log%"'
) do if %%a GEQ 1 set Anz=____%%a& echo %%i!Anz:~-4!>>"%Erg%"
Technisch gesehen ist der For Befehl mit den beiden nächsten eingerückten Zeilen ein einziger Befehl.
Das ^ als letztes Zeichen Escaped das Return und der For Befehl kann hinter der öffnenden bzw. vor
der schließenden Klammer einfach in der nächsten Zeile fortgesetzt werden da der Cmd-Interpreter
weitere Information voraussetzt. Diese Schreibweise erleichtert das posten speziel in newsgroups mit
max ~72 Zeichen/Zeile
Type NUL >"%Erg%"
Zum Erzeugen der Zeilen-Datei im Minuten-Abstand:
:: GenZeiten.cmd
@echo off&setlocal EnableDelayedExpansion
set "Zeiten=.\Zeiten.txt"
if Not exist "%Zeiten%"^
for /L %%h in (100 1 123
) do for /L %%m in (100 1 159
) do set _=%%h%%m&echo !_:~1,2!:!_:~4,2!:00>>%Zeiten%
Gruß
Zitat von @bastla:
Die Aufteilung der Zeilen ist sicherlich zu einem gewissen Teil auch Geschmackssache -
ich finde meine bisher verwendete Schreibweise übersichtlich genug und werde dabei bleiben.
Die Aufteilung der Zeilen ist sicherlich zu einem gewissen Teil auch Geschmackssache -
ich finde meine bisher verwendete Schreibweise übersichtlich genug und werde dabei bleiben.
Ist mir klar, deswegen schrub ich ja auch Verschlimmbesserung
Gruß
LotPings