SimonS
SimonS

Reputation: 1973

Search for certain strings in a file, without looping through the content twice

I have a list of log files, these log files get created every day at 00:00:00, so each day gets logged in a seperate log file. 4 Log Files for 4 different reasons get created everyday, and I have to loop over them and select the error messages.

The log files have the following structure:

07.11.2016 12:00:41  Flag: Mandant=1, Modul=V, Pool=5
07.11.2016 12:00:41  Verarbeiten: M1, V, Pool 5, Dok 526198(), DokTyp 3, Skript BU-CO110.FF, Drucker 97
07.11.2016 12:00:41  fataler Fehler SalDocPoolItem_Process(5,): Err-226/VER-Dokument  nicht gefunden:           0

07.11.2016 12:00:57  Flag: Mandant=1, Modul=V, Pool=5
07.11.2016 12:00:57  Verarbeiten: M1, V, Pool 5, Dok 526198(), DokTyp 3, Skript BU-CO110.FF, Drucker 97
07.11.2016 12:00:57  fataler Fehler SalDocPoolItem_Process(5,): Err-226/VER-Dokument  nicht gefunden:           0

07.11.2016 12:01:13  Flag: Mandant=1, Modul=V, Pool=5
07.11.2016 12:01:13  Verarbeiten: M1, V, Pool 5, Dok 526198(), DokTyp 3, Skript BU-CO110.FF, Drucker 97
07.11.2016 12:01:13  fataler Fehler SalDocPoolItem_Process(5,): Err-226/VER-Dokument  nicht gefunden:           0

As you can see theres a message "fataler fehler" on the bottom of each block, so I need to get this whole block into an array, and then send all "fataler Fehler" blocks of all log files together in a simple E-Mail.

Now I basically have the code, but this code either has a mistake, or needs more than 10 minutes to execute, since I ran it 5 minutes before starting to write this question, and it's still not finished.

Goal: Have all the blocks in the $FailArray Variable

Can you tell me a better way to achieve my goal? I really don't like using get-content twice because I feel like it's not necessary, I'm sure there's a better solution to this than mine:

# Prepare Array
$FailArray = @()

# Search for Log Files
$a = gci "C:\path" -filter *.log | ? { $_.LastWriteTime -ge (get-date).AddDays('-1') }

# Loop over Log Files
$a | % {
    # Are there errors in the log?
    $x = get-content $_.FullName | ? { $_ -like "*Fehler*" }
    # If there are errors in the block, get the "time stamp" of the Block, like 12:00:57
    if ($x) { $y = $x | % { $_.split(' ')[1] } } else { return }
    # Search the whole block, depending on the timestamp
    $z = get-content $_.FullName | ? { $_ -like "*$y*" }
    # Add found blocks to FailArray
    $FailArray += $z
}

More: Sometimes, the first message of the block has a timestamp 1 second different from the others like this - is it possible to still get the whole block somehow? (just a nice to have, not necessary)

07.11.2016 12:00:24  Flag: Mandant=1, Modul=V, Pool=5
07.11.2016 12:00:25  Verarbeiten: M1, V, Pool 5, Dok 526198(), DokTyp 3, Skript BU-CO110.FF, Drucker 97
07.11.2016 12:00:25  fataler Fehler SalDocPoolItem_Process(5,): Err-226/VER-Dokument  nicht gefunden:      

Upvotes: 1

Views: 48

Answers (1)

Martin Brandl
Martin Brandl

Reputation: 59011

You can use the -context parameter and select the two lines above your match + the actual match:

$FailArray = gci "C:\path" -filter *.log | ? { $_.LastWriteTime -ge (get-date).AddDays('-1') } | % {
   Select-String -Path  $_ -Pattern 'fataler Fehler' -Context 2,0 | foreach {$_.Context.PreContext; $_.Line}
}

Upvotes: 3

Related Questions