PowerShell hat sich über die Jahre erheblich weiterentwickelt und leistungsstarke Sprachfeatures eingeführt, die das Skripting effizienter und lesbarer machen. Egal ob Sie Systemadministrator, DevOps-Ingenieur oder Entwickler sind - die Beherrschung dieser Sprachkonstrukte wird Ihre PowerShell-Produktivität dramatisch verbessern.
Variablen und Datentypen Grundlegende Variablenzuweisung # Einfache Zuweisung $name = "Max Mustermann" $alter = 30 $istAktiv = $true # Typ-Einschränkungen [string]$benutzername = "admin" [int]$anzahl = 0 [datetime]$startZeit = Get-Date Arrays und Sammlungen # Arrays $server = @("web01", "web02", "db01") $zahlen = 1..10 # Hash-Tabellen $konfiguration = @{ Server = "localhost" Port = 8080 SSL = $true } # Auf Elemente zugreifen Write-Host $server[0] # web01 Write-Host $konfiguration.Server # localhost Write-Host $konfiguration["Port"] # 8080 Erweiterte Sammlungen # ArrayList für dynamische Arrays $liste = [System.Collections.ArrayList]@() $liste.Add("element1") | Out-Null $liste.AddRange(@("element2", "element3")) # Geordnete Hash-Tabelle $geordneteKonfig = [ordered]@{ Erster = "wert1" Zweiter = "wert2" } Operatoren Vergleichsoperatoren # Grundlegende Vergleiche $a -eq $b # Gleich $a -ne $b # Ungleich $a -gt $b # Größer als $a -lt $b # Kleiner als $a -ge $b # Größer oder gleich $a -le $b # Kleiner oder gleich # String-Operationen $text -like "*muster*" # Platzhalter-Abgleich $text -match "regex" # Regulärer Ausdruck $text -contains "teilstring" # Enthält-Prüfung $text -in @("a", "b", "c") # Mitgliedschaftstest Logische Operatoren # Logische Operationen $bedingung1 -and $bedingung2 $bedingung1 -or $bedingung2 -not $bedingung # Praktisches Beispiel if (($alter -gt 18) -and ($hatFuehrerschein -eq $true)) { Write-Host "Darf Auto fahren" } Moderne Operatoren (PowerShell 7+) # Ternärer Operator $status = ($punkte -gt 80) ? "Bestanden" : "Durchgefallen" # Null-Koaleszierende Operatoren $benutzername = $env:USER ?? $env:USERNAME ?? "Unbekannt" # Null-Koaleszierende Zuweisung $konfiguration.Timeout ??= 30 Bedingte Logik If-Else-Anweisungen # Grundlegendes if-else if ($punkte -gt 90) { Write-Host "Ausgezeichnet!" } elseif ($punkte -gt 70) { Write-Host "Gute Arbeit!" } else { Write-Host "Weiter üben!" } # Einzeiler-Bedingungen if ($debug) { Write-Host "Debug-Modus aktiviert" } Switch-Anweisungen # Grundlegender Switch switch ($status) { "Running" { Write-Host "Dienst ist aktiv" } "Stopped" { Write-Host "Dienst ist inaktiv" } "Paused" { Write-Host "Dienst ist pausiert" } default { Write-Host "Unbekannter Status" } } # Erweiterter Switch mit Regex switch -Regex ($logEintrag) { "ERROR" { Write-Host "Fehler gefunden" -ForegroundColor Red } "WARNING" { Write-Host "Warnung gefunden" -ForegroundColor Yellow } "INFO" { Write-Host "Information protokolliert" } } Schleifen und Iteration ForEach-Schleifen # Traditionelle foreach foreach ($server in $server) { Test-Connection -ComputerName $server -Count 1 } # Pipeline foreach $server | ForEach-Object { Write-Host "Verarbeite $_" Test-Connection -ComputerName $_ -Count 1 } # Parallele Verarbeitung (PowerShell 7+) $server | ForEach-Object -Parallel { Test-Connection -ComputerName $_ -Count 1 } -ThrottleLimit 5 For- und While-Schleifen # For-Schleife for ($i = 0; $i -lt 10; $i++) { Write-Host "Iteration $i" } # While-Schleife $zaehler = 0 while ($zaehler -lt 5) { Write-Host "Zähler: $zaehler" $zaehler++ } # Do-While-Schleife do { $eingabe = Read-Host "Geben Sie 'beenden' ein zum Verlassen" } while ($eingabe -ne "beenden") Funktionen und Parameter Grundlegende Funktionen # Einfache Funktion function Get-SystemInfo { $os = Get-CimInstance -ClassName Win32_OperatingSystem return @{ Computername = $env:COMPUTERNAME Betriebssystem = $os.Caption FreierSpeicher = [math]::Round($os.FreePhysicalMemory / 1MB, 2) } } # Funktion aufrufen $info = Get-SystemInfo Write-Host "Computer: $($info.Computername)" Erweiterte Funktionen mit Parametern function Test-DienstStatus { [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [string[]]$DienstName, [ValidateSet("Running", "Stopped", "All")] [string]$Status = "All", [switch]$Detailliert ) process { foreach ($dienst in $DienstName) { $svc = Get-Service -Name $dienst -ErrorAction SilentlyContinue if ($svc) { if ($Status -eq "All" -or $svc.Status -eq $Status) { if ($Detailliert) { $svc | Select-Object Name, Status, StartType, DisplayName } else { "$($svc.Name): $($svc.Status)" } } } else { Write-Warning "Dienst '$dienst' nicht gefunden" } } } } # Verwendungsbeispiele Test-DienstStatus -DienstName "Spooler", "BITS" "Spooler", "BITS" | Test-DienstStatus -Status Running -Detailliert Fehlerbehandlung Try-Catch-Finally try { $inhalt = Get-Content -Path "C:\nichtvorhanden.txt" -ErrorAction Stop Write-Host "Datei erfolgreich gelesen" } catch [System.IO.FileNotFoundException] { Write-Host "Datei nicht gefunden - erstelle neue Datei" New-Item -Path "C:\nichtvorhanden.txt" -ItemType File } catch { Write-Host "Ein unerwarteter Fehler ist aufgetreten: $($_.Exception.Message)" } finally { Write-Host "Aufräumarbeiten abgeschlossen" } Fehleraktions-Einstellungen # Globale Fehlereinstellung setzen $ErrorActionPreference = "Stop" # Pro-Befehl Fehlerbehandlung Get-Process -Name "Nichtvorhanden" -ErrorAction SilentlyContinue Get-Service -Name "Ungueltig" -ErrorAction Ignore # Fehler erfassen Get-Process -Name "Nichtvorhanden" -ErrorAction SilentlyContinue -ErrorVariable meineFehler if ($meineFehler) { Write-Host "Fehler aufgetreten: $($meineFehler.Count)" } Objektmanipulation Benutzerdefinierte Objekte erstellen # PSCustomObject (empfohlen) $server = [PSCustomObject]@{ Name = "WEB01" IP = "192.168.1.10" Rolle = "Webserver" LetzerNeustart = (Get-Date).AddDays(-7) } # Methoden zu Objekten hinzufügen $server | Add-Member -MemberType ScriptMethod -Name "GetBetriebszeit" -Value { (Get-Date) - $this.LetzerNeustart } # Methode verwenden $betriebszeit = $server.GetBetriebszeit() Write-Host "Server-Betriebszeit: $($betriebszeit.Days) Tage" Objektfilterung und -auswahl # Laufende Dienste mit spezifischen Eigenschaften abrufen $dienste = Get-Service | Where-Object { $_.Status -eq "Running" -and $_.Name -like "Win*" } | Select-Object Name, Status, StartType # Objekte gruppieren und sortieren $prozesse = Get-Process | Group-Object ProcessName | Sort-Object Count -Descending | Select-Object -First 5 # Objekte messen $speicherVerbrauch = Get-Process | Measure-Object WorkingSet -Sum -Average Write-Host "Gesamtspeicher: $([math]::Round($speicherVerbrauch.Sum / 1MB, 2)) MB" Pipeline-Operationen Pipeline-Grundlagen # Befehle mit Pipeline verketten Get-Service | Where-Object Status -eq "Running" | Sort-Object Name | Select-Object Name, Status | Format-Table -AutoSize # Pipeline-Variable $_ Get-ChildItem -Path C:\Logs -Filter "*.log" | ForEach-Object { $groesse = [math]::Round($_.Length / 1KB, 2) Write-Host "$($_.Name): ${groesse} KB" } Erweiterte Pipeline-Techniken # Tee-Object für Pipeline-Verzweigung Get-Process | Tee-Object -FilePath "prozesse.txt" | Where-Object CPU -gt 100 | Sort-Object CPU -Descending # Out-GridView für interaktive Filterung Get-EventLog -LogName System -Newest 100 | Out-GridView -Title "Systemereignisse" -PassThru | Export-Csv -Path "ausgewaehlte_ereignisse.csv" Klassen und Methoden Klassen definieren class Server { [string]$Name [string]$IP [string]$Rolle [datetime]$LetzerNeustart # Konstruktor Server([string]$name, [string]$ip, [string]$rolle) { $this.Name = $name $this.IP = $ip $this.Rolle = $rolle $this.LetzerNeustart = Get-Date } # Methoden [timespan]GetBetriebszeit() { return (Get-Date) - $this.LetzerNeustart } [void]Neustart() { Write-Host "Starte Server $($this.Name) neu..." $this.LetzerNeustart = Get-Date } [string]ToString() { return "$($this.Name) ($($this.IP)) - $($this.Rolle)" } } # Klasse verwenden $webServer = [Server]::new("WEB01", "192.168.1.10", "Webserver") Write-Host $webServer.ToString() Write-Host "Betriebszeit: $($webServer.GetBetriebszeit().TotalHours) Stunden" Vererbung class DatenbankServer : Server { [string]$DatenbankEngine [int]$Port DatenbankServer([string]$name, [string]$ip, [string]$engine, [int]$port) : base($name, $ip, "Datenbank") { $this.DatenbankEngine = $engine $this.Port = $port } [void]DatenbankSichern() { Write-Host "Sichere $($this.DatenbankEngine) Datenbank auf $($this.Name)" } } $dbServer = [DatenbankServer]::new("DB01", "192.168.1.20", "SQL Server", 1433) $dbServer.DatenbankSichern() Moderne Sprachfeatures Splatting # Parameter-Splatting $parameter = @{ Path = "C:\Logs" Filter = "*.log" Recurse = $true } Get-ChildItem @parameter # Array-Splatting $argumente = @("notepad.exe", "C:\temp\datei.txt") Start-Process @argumente String-Interpolation und Here-Strings # String-Interpolation $name = "Hans" $alter = 30 Write-Host "Hallo, $name! Du bist $alter Jahre alt." # Unterausdrucks-Operator Write-Host "Aktuelle Zeit: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" # Here-Strings für mehrzeiligen Text $skript = @" Get-Service | Where-Object { $_.Status -eq "Running" } | Select-Object Name, Status "@ # Here-String als Skriptblock ausführen Invoke-Expression $skript Reguläre Ausdrücke # Grundlegender Regex-Abgleich $text = "Fehler: Verbindung fehlgeschlagen um 2024-01-15 10:30:25" if ($text -match "(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})") { Write-Host "Zeitstempel gefunden: $($Matches[1])" } # Ersetzen mit Regex $logEintrag = "FEHLER: Datenbankverbindung fehlgeschlagen" $bereinigterEintrag = $logEintrag -replace "FEHLER:", "Warnung:" # Aufteilen mit Regex $daten = "apfel,banane;orange:traube" $fruechte = $daten -split "[,;:]" Pipeline-Ketten-Operatoren (PowerShell 7+) # Erfolgskette (&&) Get-Service -Name "Spooler" && Write-Host "Spooler-Dienst existiert" # Fehlerkette (||) Get-Service -Name "Nichtvorhanden" 2>$null || Write-Host "Dienst nicht gefunden" # Ketten kombinieren Test-Path "C:\temp" && Write-Host "Temp existiert" || Write-Host "Temp fehlt" Praktische Beispiele System-Überwachungsskript function Get-SystemGesundheit { [CmdletBinding()] param( [string[]]$ComputerName = $env:COMPUTERNAME, [int]$CPUSchwellwert = 80, [int]$SpeicherSchwellwert = 90 ) $ergebnisse = foreach ($computer in $ComputerName) { try { # Systeminformationen abrufen $os = Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $computer $cpu = Get-CimInstance -ClassName Win32_Processor -ComputerName $computer # Metriken berechnen $speicherVerwendet = [math]::Round((($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / $os.TotalVisibleMemorySize) * 100, 2) $cpuAuslastung = Get-Counter "\Processor(_Total)\% Processor Time" -ComputerName $computer -SampleInterval 1 -MaxSamples 1 | Select-Object -ExpandProperty CounterSamples | Select-Object -ExpandProperty CookedValue # Ergebnisobjekt erstellen [PSCustomObject]@{ Computername = $computer CPUAuslastung = [math]::Round($cpuAuslastung, 2) SpeicherVerwendung = $speicherVerwendet Status = if ($cpuAuslastung -gt $CPUSchwellwert -or $speicherVerwendet -gt $SpeicherSchwellwert) { "Warnung" } else { "OK" } Zeitstempel = Get-Date } } catch { [PSCustomObject]@{ Computername = $computer CPUAuslastung = "Fehler" SpeicherVerwendung = "Fehler" Status = "Nicht erreichbar" Zeitstempel = Get-Date } } } return $ergebnisse } # Verwendung $gesundheitsPruefung = Get-SystemGesundheit -ComputerName @("localhost", "server01") -CPUSchwellwert 70 $gesundheitsPruefung | Format-Table -AutoSize Log-Analyseskript function Analyze-Logs { [CmdletBinding()] param( [Parameter(Mandatory)] [string]$LogPfad, [string]$Muster = "FEHLER|WARNUNG|KRITISCH", [int]$LetzteStunden = 24 ) $stichzeit = (Get-Date).AddHours(-$LetzteStunden) Get-ChildItem -Path $LogPfad -Filter "*.log" | ForEach-Object { $inhalt = Get-Content -Path $_.FullName $treffer = $inhalt | Where-Object { $_ -match $Muster } if ($treffer) { [PSCustomObject]@{ LogDatei = $_.Name FehlerAnzahl = ($treffer | Measure-Object).Count Fehler = $treffer LetztGeaendert = $_.LastWriteTime } } } | Where-Object { $_.LetztGeaendert -gt $stichzeit } | Sort-Object FehlerAnzahl -Descending } # Verwendung $logAnalyse = Analyze-Logs -LogPfad "C:\Logs" -LetzteStunden 8 $logAnalyse | Select-Object LogDatei, FehlerAnzahl, LetztGeaendert | Format-Table Best Practices und Tipps Performance-Tipps # ArrayList statt Array für häufige Hinzufügungen verwenden $liste = [System.Collections.ArrayList]@() # Anstatt: $array += $element (langsam) $liste.Add($element) | Out-Null # Viel schneller # -contains statt -in für Einzelelement-Prüfungen verwenden if ($server -contains $zielServer) { } # Schneller # Anstatt: if ($zielServer -in $server) { } # Arrays vorab zuweisen wenn Größe bekannt ist $ergebnisse = New-Object System.Object[] 1000 Code-Organisation # Regionen für große Skripte verwenden #region Hilfsfunktionen function Get-Konfiguration { # Implementierung } #endregion #region Hauptlogik # Hauptskript-Logik hier #endregion Best Practices für Fehlerbehandlung # Immer spezifische Fehlertypen verwenden wenn möglich try { # Riskante Operation } catch [System.UnauthorizedAccessException] { Write-Warning "Zugriff verweigert - Berechtigungen prüfen" } catch [System.IO.FileNotFoundException] { Write-Error "Erforderliche Datei nicht gefunden" } catch { Write-Error "Unerwarteter Fehler: $($_.Exception.Message)" throw # Weiterwerfen wenn nicht behandelbar } Fazit PowerShells Sprachfeatures bieten mächtige Werkzeuge für Automatisierung, Systemadministration und Entwicklungsaufgaben. Die modernen Features wie ternäre Operatoren, Null-Koaleszierende Operatoren und parallele Verarbeitung machen PowerShell konkurrenzfähig zu anderen Skriptsprachen, während die objektorientierte Natur und Pipeline-Philosophie erhalten bleiben.
...