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.
Dieser Spickzettel deckt PowerShell 5.1 bis 7.5 Features ab. Einige erweiterte Features erfordern PowerShell 7+ und sind möglicherweise nicht in Windows PowerShell 5.1 verfügbar.