EMZETT.
Login

Kurz: Ein typisches Admin-Skript: Logzeilen einlesen, nach Stufe und Tag auswerten und einen Bericht ausgeben.

Teil des Kurses PowerShell

Kapitel 7 von 8 im Kurs PowerShell (Abschnitt „Projekte“). Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Projekt 1: Logdatei-Report

Ein typisches Admin-Skript: Logzeilen einlesen, nach Stufe und Tag auswerten und einen Bericht ausgeben.

$log = @"
2025-03-01 10:00:01 INFO  Dienst gestartet
2025-03-01 10:05:12 ERROR Datenbank nicht erreichbar
2025-03-01 10:06:40 WARN  Wiederholungsversuch 1
2025-03-02 08:15:00 INFO  Nutzer mia angemeldet
2025-03-02 09:30:21 ERROR Zeitüberschreitung bei /api/daten
2025-03-02 09:31:05 ERROR Zeitüberschreitung bei /api/daten
2025-03-03 12:00:00 INFO  Nutzer tom angemeldet
"@ -split "`r?`n" | Where-Object { $_ }
 
$eintraege = foreach ($zeile in $log) {
    if ($zeile -match '^(?<datum>\S+) (?<zeit>\S+) (?<stufe>\w+)\s+(?<text>.*)$') {
        [pscustomobject]@{
            Datum = [datetime]$Matches.datum
            Stufe = $Matches.stufe
            Text  = $Matches.text
        }
    }
}
 
"Einträge gesamt: $($eintraege.Count)"
"--- pro Stufe"
$eintraege | Group-Object Stufe | Sort-Object Count -Descending | ForEach-Object { "{0,-6} {1}" -f $_.Name, $_.Count }
"--- Fehler pro Tag"
$eintraege | Where-Object Stufe -eq "ERROR" | Group-Object { $_.Datum.ToString("yyyy-MM-dd") } | ForEach-Object { "$($_.Name): $($_.Count)" }
"--- häufigste Fehlermeldung"
$eintraege | Where-Object Stufe -eq "ERROR" | Group-Object Text | Sort-Object Count -Descending | Select-Object -First 1 | ForEach-Object { "$($_.Name) ($($_.Count)x)" }
"--- Nutzer"
($eintraege | Where-Object Text -like "Nutzer*" | ForEach-Object { ($_.Text -split " ")[1] } | Sort-Object) -join ", "

Ausgabe:

Einträge gesamt: 7
--- pro Stufe
ERROR  3
INFO   3
WARN   1
--- Fehler pro Tag
2025-03-01: 1
2025-03-02: 2
--- häufigste Fehlermeldung
Zeitüberschreitung bei /api/daten (2x)
--- Nutzer
mia, tom

Projekt 2: Bestandsverwaltung mit Hashtable und CSV

$lager = @{}
$bestellungen = @(
    @{ Artikel = "Schrauben"; Menge = 500; Preis = 0.12 }
    @{ Artikel = "Muttern"; Menge = 300; Preis = 0.08 }
    @{ Artikel = "Schrauben"; Menge = 250; Preis = 0.12 }
    @{ Artikel = "Bohrer"; Menge = 12; Preis = 14.9 }
)
foreach ($b in $bestellungen) {
    if (-not $lager.ContainsKey($b.Artikel)) { $lager[$b.Artikel] = [pscustomobject]@{ Artikel = $b.Artikel; Menge = 0; Wert = 0.0 } }
    $lager[$b.Artikel].Menge += $b.Menge
    $lager[$b.Artikel].Wert += $b.Menge * $b.Preis
}
 
$bericht = $lager.Values | Sort-Object Wert -Descending
$bericht | ForEach-Object { "{0,-10} {1,5} Stück {2,9:N2} EUR" -f $_.Artikel, $_.Menge, $_.Wert }
"Gesamtwert: {0:N2} EUR" -f ($bericht | Measure-Object Wert -Sum).Sum
 
$csv = $bericht | ConvertTo-Csv -NoTypeInformation
$csv.Count
($csv | ConvertFrom-Csv | Where-Object { [double]$_.Wert -gt 50 }).Artikel -join ", "

Ausgabe:

Bohrer        12 Stück    178.80 EUR
Schrauben    750 Stück     90.00 EUR
Muttern      300 Stück     24.00 EUR
Gesamtwert: 292.80 EUR
4
Bohrer, Schrauben

Projekt 3: Backup-Skript mit Rotation

function Invoke-Backup {
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)][string]$Quelle,
        [Parameter(Mandatory)][string]$Ziel,
        [int]$Behalten = 3,
        [string]$Stempel = (Get-Date -Format "yyyyMMdd-HHmmss")
    )
    if (-not (Test-Path $Quelle)) { throw "Quelle fehlt: $Quelle" }
    New-Item -ItemType Directory -Path $Ziel -Force | Out-Null
    $name = "backup-$Stempel"
    $zielDatei = Join-Path $Ziel "$name.zip"
    if ($PSCmdlet.ShouldProcess($zielDatei, "Sicherung erstellen")) {
        Compress-Archive -Path (Join-Path $Quelle "*") -DestinationPath $zielDatei
    }
    $alte = Get-ChildItem $Ziel -Filter "backup-*.zip" | Sort-Object Name -Descending | Select-Object -Skip $Behalten
    $alte | Remove-Item
    "erstellt: $name.zip, gelöscht: $($alte.Count)"
}
 
$basis = Join-Path ([IO.Path]::GetTempPath()) "psbackup_$(Get-Random)"
$quelle = Join-Path $basis "daten"
$ziel = Join-Path $basis "sicherungen"
New-Item -ItemType Directory $quelle -Force | Out-Null
"wichtig" | Set-Content (Join-Path $quelle "a.txt")
 
1..5 | ForEach-Object { Invoke-Backup -Quelle $quelle -Ziel $ziel -Behalten 3 -Stempel "2025030$_" }
(Get-ChildItem $ziel | Sort-Object Name).Name
(Get-ChildItem $ziel).Count
try { Invoke-Backup -Quelle "/gibt/es/nicht" -Ziel $ziel } catch { "Fehler: $($_.Exception.Message)" }
Remove-Item $basis -Recurse -Force

Ausgabe:

erstellt: backup-20250301.zip, gelöscht: 0
erstellt: backup-20250302.zip, gelöscht: 0
erstellt: backup-20250303.zip, gelöscht: 0
erstellt: backup-20250304.zip, gelöscht: 1
erstellt: backup-20250305.zip, gelöscht: 1
backup-20250303.zip
backup-20250304.zip
backup-20250305.zip
3
Fehler: Quelle fehlt: /gibt/es/nicht

Mit -WhatIf (z. B. Invoke-Backup -Quelle ... -Ziel ... -WhatIf) zeigt PowerShell nur an, was passieren würde. ShouldProcess und -WhatIf / -Confirm gehören zu jedem Skript, das etwas verändert: Nutzer können gefahrlos testen.

Projekt 4: Pester-Test für eine Funktion

Das Test-Framework Pester (in PowerShell enthalten oder per Install-Module Pester) prüft Funktionen:

BeforeAll {
    function Get-Rabatt([double]$Preis, [int]$Prozent) { [math]::Round($Preis * (1 - $Prozent / 100), 2) }
}
 
Describe "Get-Rabatt" {
    It "berechnet 10 Prozent Rabatt" {
        Get-Rabatt -Preis 50 -Prozent 10 | Should -Be 45
    }
    It "gibt bei 0 Prozent den Originalpreis" {
        Get-Rabatt 19.99 0 | Should -Be 19.99
    }
    It "wirft bei Text einen Fehler" {
        { Get-Rabatt "abc" 10 } | Should -Throw
    }
}

Ausführen mit Invoke-Pester. Schreibe Tests, bevor Skripte produktiv laufen.

Merke

  • Regex mit benannten Gruppen ($Matches.datum) macht Log-Auswertung einfach
  • [pscustomobject] baut eigene Datensätze; Group-Object, Measure-Object fassen zusammen
  • [CmdletBinding(SupportsShouldProcess)] mit -WhatIf für sichere Skripte
  • Pester testet Funktionen
  • Skripte lassen sich plattformübergreifend mit pwsh ausführen

Übungsaufgabe

Schreibe ein Skript, das alle .log-Dateien eines Ordners nach Fehlerzeilen durchsucht und eine Zusammenfassung ausgibt.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Dateien, Text, Regex, Module und Remoting

Weiter: Referenz und Spickzettel

Alle Kapitel: PowerShell im Überblick