<# .SYNOPSIS Führt PowerShell-Blöcke in einer HTML-Vorlage aus und ersetzt Platzhalter mit den Ergebnissen. .DESCRIPTION Sucht nach Codeblöcken der Form: (Ausgabe wird HTML-kodiert eingefügt) sowie (Ausgabe wird ungefiltert/roh eingefügt, z.B. für ConvertTo-Html -Fragment). Alle Blöcke werden in derselben Runspace/Session ausgeführt (Variablenzustand bleibt erhalten). .PARAMETER Path Pfad zur Eingabe-HTML-Vorlage. .PARAMETER OutFile Optionaler Ausgabepfad. Wenn nicht angegeben, wird das Ergebnis auf StdOut geschrieben. .PARAMETER Encoding Text-Encoding der Ausgabe (Standard: UTF8 ohne BOM). .PARAMETER Init Optionales Initialisierungsskript (String), das vor den Platzhaltern einmalig ausgeführt wird. .EXAMPLE .\Invoke-HtmlPsTemplate.ps1 -Path .\template.html -OutFile .\status.html #> [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string]$Path, [Parameter(Mandatory=$false)] [string]$OutFile, [ValidateSet('UTF8','UTF8BOM','ASCII','Unicode','UTF7','UTF32','BigEndianUnicode','Default','OEM')] [string]$Encoding = 'UTF8', [string]$Init ) begin { Write-Host "Lade Vorlage: $Path" if (-not (Test-Path -LiteralPath $Path)) { throw "Vorlage nicht gefunden: $Path" } Write-host "extrahiere Codeblöcke..." $regexPs = [Regex]::new('<\?PS\s*(?.*?)\s*\?>', 'Singleline,IgnoreCase') $regexPsRaw = [Regex]::new('<\?PS-RAW\s*(?.*?)\s*\?>', 'Singleline,IgnoreCase') $htmlEncode = { param([string]$s) [System.Net.WebUtility]::HtmlEncode($s) } Write-host "initialisiere PowerShell-Session..." # Gemeinsame PowerShell-Session $ps = [System.Management.Automation.PowerShell]::Create() $null = $ps.AddScript('$ErrorActionPreference = "Stop"') if ($Init) { Write-Host "Führe Init-Skript aus..." $null = $ps.AddScript($Init) try { $null = $ps.Invoke() } catch { $err = $_ $ps.Dispose() throw "Fehler im Init-Skript: $($err.Exception.Message)" } finally { $ps.Commands.Clear() } } function Invoke-CodeBlock { param( [Parameter(Mandatory)] [System.Management.Automation.PowerShell]$PsSession, [Parameter(Mandatory)] [string]$Code, [Parameter(Mandatory)] [bool]$Raw ) Write-Host "Führe Codeblock aus (Raw=$Raw): $Code" $PsSession.Commands.Clear() | Out-Null $null = $PsSession.AddScript($Code) try { $output = $PsSession.Invoke() $errs = $PsSession.Streams.Error if ($errs.Count -gt 0) { $errText = ($errs | ForEach-Object { $_.ToString() }) -join [Environment]::NewLine throw "Fehler im Codeblock: $errText" } # Konsistente Textdarstellung if ($Raw) { if ($output -and ($output | Where-Object { -not ($_ -is [string]) })) { $PsSession.Commands.Clear() | Out-Null $null = $PsSession.AddScript('$args | Out-String -Width 4096').AddArgument($output) $txt = $PsSession.Invoke() -join '' return $txt } else { return ($output -join '') } } else { if ($output -and ($output | Where-Object { -not ($_ -is [string]) })) { $PsSession.Commands.Clear() | Out-Null $null = $PsSession.AddScript('$args | Out-String -Width 4096').AddArgument($output) $txt = $PsSession.Invoke() -join '' } else { $txt = ($output -join '') } return (& $htmlEncode $txt) } } catch { $msg = $_.Exception.Message $msgHtml = (& $htmlEncode $msg) if ($Raw) { return "
<PS-RAW Error> $msgHtml
" } else { return "<PS Error> $msgHtml" } } finally { $PsSession.Commands.Clear() | Out-Null $PsSession.Streams.Error.Clear() $PsSession.Streams.Warning.Clear() $PsSession.Streams.Debug.Clear() $PsSession.Streams.Verbose.Clear() $PsSession.Streams.Progress.Clear() } } } process { Write-host "Lese Vorlage und ersetze Codeblöcke..." $template = Get-Content -LiteralPath $Path -Raw # Erst RAW ersetzen $result = $regexPsRaw.Replace($template, { param($m) $code = $m.Groups['code'].Value Invoke-CodeBlock -PsSession $ps -Code $code -Raw:$true }) # Dann normal (HTML-kodiert) $result = $regexPs.Replace($result, { param($m) $code = $m.Groups['code'].Value Invoke-CodeBlock -PsSession $ps -Code $code -Raw:$false }) if ($OutFile) { $encMap = @{ UTF8 = New-Object System.Text.UTF8Encoding($false) UTF8BOM = New-Object System.Text.UTF8Encoding($true) ASCII = [System.Text.Encoding]::ASCII Unicode = [System.Text.Encoding]::Unicode UTF7 = [System.Text.Encoding]::UTF7 UTF32 = [System.Text.Encoding]::UTF32 BigEndianUnicode = [System.Text.Encoding]::BigEndianUnicode Default = [System.Text.Encoding]::Default OEM = [System.Text.Encoding]::GetEncoding([Console]::OutputEncoding.CodePage) } $enc = $encMap[$Encoding] Write-Host "Speichere Ausgabe in: $OutFile (Encoding: $Encoding)" [IO.File]::WriteAllText(($OutFile), $result, $enc) Write-Verbose "Ausgabe gespeichert in: $OutFile" } else { $result } } end { if ($ps) { $ps.Dispose() } }