basebubble
Goto Top

Aufteilung einer Logdatei anhand bestimmter Zeichenkette

Hallo!

Ich habe jetzt schon eine Weile gesucht aber bisher noch keine passende Lösung gefunden, ich hoffe, Ihr könnt mir helfen!

Eine Fachsoftware erzeugt bei Abarbeitung mehrerer gleichartigere Jobs eine zusammenhängende Logdatei. Es ist leider nicht möglich, für jeden Job eine eigene Log zu erstellen.

Die als *.log vorliegende Textdatei soll ich in eine auswertbare Excel-Tabelle umwandeln. Teilweise werden Daten für alle Datensätze der Excel-Tabelle nur aus der Überschrift der Log geholt. Die Log-Datei hat ungefähr folgende Struktur:
20180813 14:06 || VeraenderungDatenJob || ##########################
20180813 14:06 || VeraenderungDatenJob || Starte Job "VeraenderungDatenJob".  
20180813 14:06 || VeraenderungDatenJob || ##########################
20180813 14:06 || VeraenderungDatenJob || Die Daten XY werden durch die Daten YZ ersetzt:
20180813 14:06 || VeraenderungDatenJob ||    NameYZ
20180813 14:06 || VeraenderungDatenJob || YZ hat die Telefonnummer: 123456789
20180813 14:06 || VeraenderungDatenJob || Folgende Personen sind hiervon betroffen
20180813 14:06 || VeraenderungDatenJob || Name: Karl Koch
20180813 14:06 || VeraenderungDatenJob || Name: Liese Müller
20180813 14:06 || VeraenderungDatenJob || Name: Willi Wuppel
...
20180813 14:06 || VeraenderungDatenJob || ##########################
20180813 14:06 || VeraenderungDatenJob || Der Job wurde erfolgreich beendet.
20180813 14:06 || VeraenderungDatenJob || ##########################
20180813 14:06 || VeraenderungDatenJob || ##########################
20180813 14:06 || VeraenderungDatenJob || Starte Job "VeraenderungDatenJob".  
20180813 14:06 || VeraenderungDatenJob || ##########################
20180813 14:06 || VeraenderungDatenJob || Die Daten ÄÖ werden durch die Daten BC ersetzt:
20180813 14:06 || VeraenderungDatenJob ||    NameBC
20180813 14:06 || VeraenderungDatenJob || BC hat die Telefonnummer: 987654321
20180813 14:06 || VeraenderungDatenJob || Folgende Personen sind hiervon betroffen
20180813 14:06 || VeraenderungDatenJob || Name: Hansi Hoppel
20180813 14:06 || VeraenderungDatenJob || Name: Erna P.
20180813 14:06 || VeraenderungDatenJob || Name: Helga Husten
...
20180813 14:06 || VeraenderungDatenJob || ##########################
20180813 14:06 || VeraenderungDatenJob || Der Job wurde erfolgreich beendet.
20180813 14:06 || VeraenderungDatenJob || ##########################

Im Beispiel besteht die Log-Datei aus zwei Jobs, es können aber auch erheblich mehr sein. Für jeden Job soll eine eigene Excel-Tabelle erstellt werden, da die Daten aus den Zeilen 5 und 6 in die Spalten der einzelnen betroffenen Namen eingefügt werden sollen.
Da ich jeweils nie weiß, wieviele betroffene Namen vorhanden sind, kann ich nicht einfach sagen: "Starte den Import dieses Mal in Zeile xxx." Es sind teilweise über 1000 Namen betroffen.

Ich nehme an, es würde am meisten Sinn machen, vor der Verarbeitung via Excel-Makro die Text-Datei per Batch in mehrere einzelne Text-Dateien aufzusplitten. Ich habe leider nur sehr rudimentäre Erfahrungen in Batch-Programmierung und vor allem zwei Fragen stehen nun im Raum.

1. Wie trenne ich die Datei jeweils an den Stellen zwischen den Rauten-Linien
2. Wie speichere ich die einzelnen Teile unter einem möglichst aussagekräftigen Namen (am Besten dem aus Zeile 5 des jeweiligen Jobs)

Ich hoffe, meine Fragestellung ist einigermaßen klar und nicht zu verworren. Ich sitze da jetzt schon ewig vor und das Hirn überhitzt langsam... face-smile

Content-Key: 383909

Url: https://administrator.de/contentid/383909

Ausgedruckt am: 19.03.2024 um 05:03 Uhr

Mitglied: NetzwerkDude
NetzwerkDude 20.08.2018 aktualisiert um 16:08:48 Uhr
Goto Top
Weiß nicht wie das in cmd zu lösen wäre, aber mit PowerShell (ebenfalls bestandteil von Windows face-smile ) sollte es gehen
Glaube die Antwort auf beide Fragen ist: RegEx + paar Operatoren aus Powershell

#Inhalt des Logs einlesen, und der Variable InhaltRAW zuweisen
$InhaltRAW = Get-Content .\logdatei.log -Raw

#Die lange Datei nach dem String "Der Job wurde erfolgreich beendet." aufteilen, das Ergebnis ist ein Array aus den einzelnen Abschnitten
$ArrayOfLog = $InhaltRAW -split '(?<=Der Job wurde erfolgreich beendet.)'  

#Die einzelnen Logs aus dem Array in einzelne, nummerierte Dateien Speichern:
$ArrayOfLog | ForEach-Object -Begin {$i=1} -Process {$_ > "log$i.txt"; $i++}  

PS: Du kannst ja noch nach einem besseren String zu trennen suchen wenn du magst - einfach alles hinter dem "?<=" ersetzen bis zu der schließenden klammer

MFG N-Dude

Edit:
Hier der richtige Schritt3, falls du die einzelnen Logs auch richtig benannt haben willst:
$ArrayOfLog | ForEach-Object -Process {if ($_ -match '\bName\w{2}\b') {$Name = $Matches.Values; $_ > "$Name.txt"}}  
Mitglied: BaseBubble
BaseBubble 21.08.2018 um 11:04:15 Uhr
Goto Top
Hallo! Danke für den Lösungsvorschlag!
Powershell habe ich auf dem Rechner hier leider nicht zur Verfügung. face-sad
Mitglied: Friemler
Friemler 21.08.2018, aktualisiert am 22.08.2018 um 15:01:02 Uhr
Goto Top
Hallo BaseBubble,

ich habe hier mal ein Batchscript zum Zerlegen der Log-Datei in Einzeldateien zusammengeschrotet:
@echo off & setlocal

set "InFile=.\Xyz.log"  
set "TmpFile=%TEMP%\BlockData.txt"  
set "OutFilePath=."  


set "OutFile=%OutFilePath%\ThisIsAnError.txt"  
set "StartBlock="  
set "InBlock="  
set "LineCnt="  

chcp 1252 > NUL

del "%TmpFile%" 1>NUL 2>NUL  

for /f "usebackq tokens=4 delims=|" %%a in ("%InFile%") do (  
  echo %%~a|findstr /ibr /c:" *Starte Job" > NUL && (  
    set "StartBlock=1"  
    set "InBlock="  
    set "LineCnt="  
  )

  echo %%~a|findstr /ir /c:"^ *##*$" > NUL && (  
    if defined StartBlock (
      set "StartBlock="  
      set "InBlock=1"  
      set "LineCnt="  
    ) else (
      set "InBlock="  
      set "LineCnt="  
      call :MoveFile
    )
  )

  if defined InBlock (
    if not defined LineCnt (
      set /a LineCnt=0
    ) else (
      set /a LineCnt+=1
      call :ProcessLine "%%~a"  
    )
  )
)

exit /b 0



:ProcessLine
  for /f "tokens=*" %%z in ("%~1") do (  
    if "%LineCnt%" equ "2" (  
      set "OutFile=%OutFilePath%\%%~z.txt"  
    )

    echo >>"%TmpFile%" %%~z  
  )
exit /b 0


:MoveFile
  setlocal enabledelayedexpansion

  set /a FileCnt=1

  if exist "%OutFile%" (  
    for %%z in ("%OutFile%") do (  
      for %%y in ("%%~dpnz*%%~xz") do (  
        set /a FileCnt+=1
        set "OutFile=%%~dpnz_(!FileCnt!)%%~xz"  
      )
    )
  )

  move "%TmpFile%" "%OutFile%" 1>NUL 2>NUL  

  endlocal
exit /b 0

In Zeile 3 musst Du den Pfad zur Log-Datei eintragen und in Zeile 5 das Ausgabeverzeichnis (nur den Pfad, ohne abschließenden Backslash!)

Das Script extrahiert nur die Inhalte aus Spalte 4 des Logs und dürfte bei einer großen Log-Datei nicht sehr performant sein.

Die Dateien werden nach dem Inhalt von Zeile 5 jedes Jobs benannt. Sollten mehrere Jobs vorhanden sein, deren Zeile 5 identisch ist und somit zu gleichen Dateinamen führen würden, werden die Dateinamen mit Zählern versehen (NameYZ.txt, NameYZ_(2).txt, NameYZ_(3).txt, usw.).

Ich gehe mal davon aus, dass die Log-Datei in der Zeichen-Codierung Windows-1252 (ANSI) gespeichert wurde. Aus diesem Grund habe ich dafür gesorgt (Zeile 13), dass die Datei auch mit dieser Codierung eingelesen wird. Das ist wichtig, damit die Ausgabedateien auch dann korrekte Namen erhalten, wenn in Zeile 5 der Job-Daten deutsche Umlaute vorkommen.

Grüße
Friemler
Mitglied: BaseBubble
BaseBubble 22.08.2018 aktualisiert um 11:30:11 Uhr
Goto Top
Boah! Super, danke!
Werde ich gleich mal ausprobieren...
Melde mich, ob's geklappt hat.

...

Ok, hat jetzt erstmal nicht geklappt, liegt aber nicht an Deiner Batch sondern an meinen Infos. Der String "Starte Job" befindet sich immer in der vierten Spalte! Die Struktur des Logs sieht tatsächlich so aus:

20180813 14:06 || INFO || VeraenderungDatenJob || ########################## 
20180813 14:06 || INFO || VeraenderungDatenJob || Starte Job "VeraenderungDatenJob".   
20180813 14:06 || INFO || VeraenderungDatenJob || ########################## 
20180813 14:06 || INFO || VeraenderungDatenJob || Die Daten XY werden durch die Daten YZ ersetzt: 
.....

Ich finde nur leider nicht die Stelle, an der Du die zu durchsuchende Spalte definiert hast. Könntest Du mir da bitte nochmal einen Schubs in die richtige Richtung geben?
Danke!
Mitglied: Friemler
Friemler 22.08.2018 aktualisiert um 14:36:42 Uhr
Goto Top
Hi,

ich habe mein Script oben angepasst, jetzt wird nur der Inhalt der Spalte 4 extrahiert. Gesteuert wird das über die TOKENS-Option der FOR-Schleife. Siehe auch mein Tutorial zur FOR-Schleife.

Außerdem habe ich das Script gegen das Überschreiben von Ausgabedateien mit gleichem Namen abgesichert und entsprechende Hinweise ergänzt.

Grüße
Friemler
Mitglied: colinardo
colinardo 22.08.2018 aktualisiert um 16:18:05 Uhr
Goto Top
Oder wenn du Plain Excel bevorzugst, hier als VBA zusammengeschrotet. Wie du das im Sheet aufgeteilt haben wolltest kann ich anhand deiner Beschreibung leider nicht ganz deuten, deshalb erst mal nur grundlegend auf Spalten verteilt.

screenshot
Sub ProcessLog()
    Dim arrNames() As Variant, cnt As Long, fso As Object, rngOut As Range, regex As Object, blocks As Object, block As Object
    ' Pfad zur Logdatei  
    Const LOGFILE = "D:\Logfiles\MeinMegaLog.log"  
    'Objekte  
    Set fso = CreateObject("Scripting.FileSystemObject")  
    Set regex = CreateObject("vbscript.regexp")  
    ' Regex Optionen festlegen  
    regex.Global = True: regex.IgnoreCase = True: regex.MultiLine = True
    regex.Pattern = "Starte Job[\s\S]*?#{5,}[\r\n]+([\s\S]*?)[^\r\n]+#{5,}"  
    
    ' Inhalt des Logs auslesen  
    strContent = fso.OpenTextFile(LOGFILE, 1).ReadAll()
    
    ' Ausgabe startet in dieser Zeile  
    Set rngOut = Range("A2")  
    ' Überschriften setzen  
    With Range("A1:C1")  
        .Value = Array("Name", "Daten", "Namen")  
        .Font.Bold = True
    End With
    ' Regex ausführen  
    Set blocks = regex.Execute(strContent)
    
    ' neuen Regex Pattern für die Namen festlegen  
    regex.Pattern = " \|\| Name:\s*(.*)"  
    
    ' Für jeden Logabschnitt  
    For Each block In blocks
        ' Name und Daten auslesen  
        arrLines = Split(block.submatches(0), vbNewLine, -1)
        strName = Trim(Split(arrLines(1), "||", 4, vbTextCompare)(3))  
        strDaten = Trim(Split(arrLines(2), "||", 4, vbTextCompare)(3))  
        
        ' betroffene Namen auslesen und in Array schreiben  
        cnt = 1
        Set matchedNames = regex.Execute(block.submatches(0))
        For Each n In matchedNames
            ReDim Preserve arrNames(1 To cnt)
            arrNames(cnt) = Trim(n.submatches(0))
            cnt = cnt + 1
        Next
        ' Name und Daten ausgeben  
        rngOut.Resize(1, 2).Value = Array(strName, strDaten)
        ' betroffene Namen in Spalten ausgeben  
        rngOut.Offset(0, 2).Resize(1, UBound(arrNames)).Value = arrNames
        ' move output range to next line  
        Set rngOut = rngOut.Offset(1, 0)
    Next
End Sub
Grüße Uwe