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

Kommentiertes Batch-Beispiel - Auswertung von Logdateien nach Zeiten

Anleitung Entwicklung Batch & Shell

Mitglied: bastla

bastla (Level 5) - Jetzt verbinden

11.08.2008, aktualisiert 17.10.2012, 25895 Aufrufe, 6 Kommentare

Hallo @All!

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 http://www.administrator.de/frage/suchen-von-strings-mit-von-batch-file ... ):
Zwei Logdateien ("D:\Zeiten.txt", "D:\Gesamtlog.txt") enthalten Zeitangaben bzw zugeordnete Ereignisse.

Aufbau der Datei "Zeiten":
01.
00:00:00 
02.
00:01:00 
03.
00:02:00 
04.
00:03:00 
05.
00:04:00 
06.
00:05:00 
07.
00:06:00 
08.
........ 
09.
23:59:00
Inhalt von "Gesamtlog":
Zu jeder in "Zeiten" enthaltenen Uhrzeit können mehrere Zeilen (mit der Zeit und zusätzlichen Informationen) vorkommen, zB:
01.
00:02:00 Ereigniskennung: 68 
02.
00:02:00 Ort: K-1-08 
03.
00:02:00 Zusatz: - 
04.
00:05:00 Ereigniskennung: 07 
05.
00:05:00 Ort: G-3-02 
06.
00:05:00 Zusatz: Hier könnten zusätzliche Informationen, 
07.
00:05:00 Zusatz: auch auf mehrere Zeilen verteilt, 
08.
00:05:00 Zusatz: protokolliert werden - den Text der 
09.
00:05:00 Zusatz: übrigen 8 Zeilen spare ich ein ;-) 
10.
00:05:00 Zusatz:  
11.
00:05:00 Zusatz:  
12.
00:05:00 Zusatz:  
13.
00:05:00 Zusatz:  
14.
00:05:00 Zusatz:  
15.
00:05:00 Zusatz:  
16.
00:05:00 Zusatz:  
17.
00:05:00 Zusatz:  
18.
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:
01.
00:00:00___0 
02.
00:01:00___0 
03.
00:02:00___3 
04.
00:03:00___0 
05.
00:04:00___0 
06.
00:05:00__14 
07.
00:06:00___0 
08.
........___0 
09.
23:59:00___0
Diese Ausgabe lässt sich mit folgendem Batch erreichen:
01.
@echo off & setlocal enabledelayedexpansion 
02.
set "Zeiten=D:\Zeiten.txt" 
03.
set "Log=D:\Gesamtlog.txt" 
04.
 
05.
for /f "usebackq delims=" %%i in ("%Zeiten%") do ( 
06.
    set /a Anzahl=0 
07.
    for /f %%a in ('findstr /c:"%%i" "%Log%"') do set /a Anzahl+=1 
08.
    set "Formatiert=____!Anzahl!" 
09.
    echo %%i!Formatiert:~-4! 
10.
)
Zur Funktion der einzelnen Zeilen:
@echo off & setlocal enabledelayedexpansion
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.

set "Zeiten=D:\Zeiten.txt" 
set "Log=D:\Gesamtlog.txt"
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.

for /f "usebackq delims=" %%i in ("%Zeiten%") do (
Schleife, um alle Zeilen der Zeiten-Tabelle einzeln in der Variable %%i zu erhalten.
  • "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
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).

for /f %%a in ('findstr /c:"%%i" "%Log%"') do set /a Anzahl+=1
Schleife über alle Zeilen, welche %%i (die vorhin ausgelesene Uhrzeit) enthalten
  • "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!"
Zwischenspeichern der ermittelten Anzahl und "Auffüllen" von links her mit "_"
  • 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!
Ausgabe der Uhrzeit und der (formatierten) Anzahl der Fundstellen
  • 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:
)
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:
---------- D:\GESAMTLOG.TXT: 14
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:
---------- ..\AKTUELLE LOGS\GESAMTLOG EREIGNISSE.TXT: 14
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:
for /f "tokens=3" %%a in ('find /c "%%i" "%Log%"') do set "Anzahl=%%a"
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
Auswertung.cmd>D:\Anzahlen.txt
oder die Umleitung in der letzten Zeile ergänzt werden:
)>>"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:
01.
@echo off & setlocal enabledelayedexpansion 
02.
set "Zeiten=D:\Zeiten.txt" 
03.
set "Log=D:\Gesamtlog.txt" 
04.
set "Erg=D:\Anzahlen.txt" 
05.
 
06.
if exist "%Erg%" del "%Erg%" 
07.
 
08.
for /f "usebackq delims=" %%i in ("%Zeiten%") do ( 
09.
    set /a Anzahl=0 
10.
    for /f %%a in ('findstr /c:"%%i" "%Log%"') do set /a Anzahl+=1 
11.
    set "Formatiert=____!Anzahl!" 
12.
    echo %%i!Formatiert:~-4! 
13.
)>>"%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
Mitglied: AndreasA
15.08.2008 um 12:28 Uhr
Hallo bastla

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
Bitte warten ..
Mitglied: sysad
19.08.2008 um 17:29 Uhr
Sehr gute Idee. Und speziell beim 'Batchen' kann man immer was von anderen lernen, weil die Befehle kurz und knackig sind und nicht wie bei einem C-Programm eben mal 10000 Zeilen 'überflogen' werden müssen.

Danke! Das musste ja auch mal gesagt werden.....
Bitte warten ..
Mitglied: LotPings
15.04.2009 um 20:57 Uhr
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.
01.
:: LogZeiten 
02.
@echo off & setlocal enabledelayedexpansion  
03.
set "Zeiten=.\Zeiten.txt"  
04.
set "Log=.\Gesamt.log"  
05.
set "Erg=.\Anzahlen.txt" 
06.
Type NUL >"%Erg%" 
07.
for /f "usebackq delims=" %%i in ("%Zeiten%" 
08.
  ) do for /f %%a in ('find /c "%%i" ^<"%Log%"' 
09.
    ) 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%"
Setzt die Dateilänge auf Null, unabhängig von der vorherigen Existenz der Datei.

Zum Erzeugen der Zeilen-Datei im Minuten-Abstand:
01.
:: GenZeiten.cmd 
02.
@echo off&setlocal EnableDelayedExpansion 
03.
set "Zeiten=.\Zeiten.txt"  
04.
if Not exist "%Zeiten%"^ 
05.
  for /L %%h in (100 1 123 
06.
    ) do for /L %%m in (100 1 159 
07.
       ) do set _=%%h%%m&echo !_:~1,2!:!_:~4,2!:00>>%Zeiten%
Gruß
Bitte warten ..
Mitglied: bastla
15.04.2009 um 21:29 Uhr
Hallo LotPings!

Danke für die Hinweise , insbesondere hinsichtlich "find /c" - ist hier zweifellos die beste Wahl (die oben in dieser Hinsicht gemachte Einschränkung gilt bei Verwendung der Eingabeumleitung - oder alternativ eines vorhergehenden type "%Log%"|, etwa für die implizite Konvertierung von Unicode-Files - dann nicht mehr) ...
Um die ursprünglich gewünschte Auswertung zu erhalten, wäre noch eine geringfügige Korrektur erforderlich:
01.
@echo off & setlocal enabledelayedexpansion  
02.
set "Zeiten=.\Zeiten.txt"  
03.
set "Log=.\Gesamt.log"  
04.
set "Erg=.\Anzahlen.txt" 
05.
Type NUL >"%Erg%" 
06.
for /f "usebackq delims=" %%i in ("%Zeiten%") do ( 
07.
    for /f %%a in ('find /c "%%i" ^<"%Log%"') do ( 
08.
        set Anz=____%%a& echo %%i!Anz:~-4!>>"%Erg%" 
09.
10.
)
Die Aufteilung der Zeilen ist sicherlich zu einem gewissen Teil auch Geschmackssache - ich finde meine bisher verwendete Schreibweise (hier nochmals für Deine Version dargestellt) übersichtlich genug und werde dabei bleiben.
Zu
Type NUL >"%Erg%"
Macht hier keinen Unterschied (da auf jeden Fall eine Ergebnisdatei entstehen wird), erzeugt aber ansonsten immer eine Datei (auch wenn nicht benötigt, da gar keine Ausgabe erfolgt), was gelegentlich (zB nachfolgendes if exist) auch kontraproduktiv sein kann ...
Das Erzeugen der Minuten-Datei war zwar nicht erforderlich, da lt ursprünglicher Problemstellung bereits vorgegeben, ist aber ein schönes Beispiel für geschachtelte Schleifen.

Grüße
bastla
Bitte warten ..
Mitglied: LotPings
15.04.2009 um 21:44 Uhr
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.

Ist mir klar, deswegen schrub ich ja auch Verschlimmbesserung

Gruß
LotPings
Bitte warten ..
Mitglied: bastla
15.04.2009 um 22:00 Uhr
Ist mir klar, deswegen schrub ich ja auch Verschlimmbesserung
... und ich frug mich schon, worauf Du Dich dabei eigentlich bezogen hattest ...

Grüße
bastla
Bitte warten ..
Neuester Wissensbeitrag
Windows 10

Powershell 5 BSOD

(8)

Tipp von agowa338 zum Thema Windows 10 ...

Ähnliche Inhalte
Batch & Shell
Batch zum bearbeiten mehrerer CSV (3)

Frage von Matzus87 zum Thema Batch & Shell ...

Batch & Shell
Batch-Variable nach Stichworten aus TXT Datei durchsuchen (3)

Frage von Markus5579 zum Thema Batch & Shell ...

Batch & Shell
Dateinamen nach Zeichnen abschneiden - Batch-Shell (9)

Frage von cberndt 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 ...