Viele meiner Scripte senden mir Daten und Auswertungen per Mail zu. Das Lesen kann je nach Datenmenge und Mailhäufigkeit schnell zum Zeitfresser werden. Daher färbe ich die Informationen gerne ein und signalisiere damit bereits beim Überfliegen der Mails, ob alles ok ist oder ob vielleicht doch Probleme aufgetreten sind. Gerade am Handy scrolle ich die Mails nur schnell nach unten. Ist alles grün, dann muss ich nicht genauer hinschauen.
Aber wie färbt man gezielt Informationen in der Darstellung? Natürlich mit HTML. Das hat zudem den Vorteil, dass gängige Mailclients den Text korrekt darstellen. Leider hat Microsoft aber kenie passenden cmdlets für meine Aufgabenstellung geliefert. Also habe ich mir eine Funktion für die Lösung erstellt. Diese möchte ich hier kurz vorstellen.
Das ist mein Ziel: Bestimmte Informationen sollen sich farblich vom Rest abheben:
Der HTML-Text dazu sieht dann so aus:
Im Header definiere ich zu Beginn einige Style-Elemente. Diese kann ich dann in den einzelnen TD-Tags (also den Tabellenzeilen) verwenden. Welche Zeile welche class zugewiesen bekommt, soll durch meine Funktion gesteuert werden.
Der finale Code wurde von mir wieder in Teilbereiche untergliedert. Zusätzlich stellte ich mir noch die Anforderung der Pipeline-Fähigkeit. Daher benötigte ich die Blöcke Begin, Process und End:
Die Parameter habe ich in 3 Teile gegliedert. Deren Verwendung zeige ich nachher praktisch an verschiedenen Beispielen.
Im Block Begin wird der HTML-Header generiert. Natürlich nur, wenn der HTML-Text noch leer ist. So kann ich auch mehrere Tabellen in einer HTML-Datei zusammenfassen, ohne dass es dublizierte Header gibt:
Im Block Process werden nur die einzelnen Zeilen der Eingabetabelle gesammelt:
Im Block End passiert das eigentliche Rendern der HTML-Tabelle. Zuerst werden die Spaltennamen in einem TH-Tag gebildet. Diese können automatisch mit get-member ausgelesen werden oder der Anwender gibt sie als Parameterargument mit an:
Dann wird die Tabelle zeilenweise erstellt. Für jede Zeile können durch Bedingungen die entsprechenden CSS-Classes bestimmt werden:
Im dritten Teil vom End-Block wird die Ausgabe vorgenommen: Wahlweise als String oder als Datei im Dateisystem:
Das ist der finale Code:
function erstelle-HTMLReport { <# .NOTES Scriptreihe: Hilfsscripte Datum: 2020-07-3 Version: V1.00 Programmierer: Stephan Walther .Synopsis Die Funktion generiert eine HTML-Tabelle aus einem PowerShell-Objekt. .DESCRIPTION Jede zweidimensionale Datenstruktur lässt sich mit ConvertTo-HTML in eine HTML-Tabelle konvertieren. Diese Funktion geht weiter und erlaubt die Generierung eines vollständigen HTML-Textes um die Tabelle inklusive dem Einsatz von CSS und Formatierungen. .EXAMPLE $Sample1 = Get-Process | select -First 10 -Property name,ws,cpu erstelle-HTMLReport -Daten $Sample1 -Headline 'Prozessliste' -Muster Es wird der vollständige Text im HTML-Format ausgegeben. .EXAMPLE $Sample1 = Get-Process | select -First 10 -Property name,ws,cpu erstelle-HTMLReport ` -Daten $Sample1 ` -Headline 'Prozessliste' ` -Sortierung 'name','ws','cpu' ` -gelb '$_.cpu -gt 10' ` -gruen '$_.cpu -lt 5' ` -rot '$_.cpu -gt 50' ` -Dateiname "$($env:USERPROFILE)\Desktop\Sample.htm" & "$($env:USERPROFILE)\Desktop\Sample.htm" Die Demodaten werden in einer html-Datei auf dem Desktop ausgegeben. Ist der Zellwert der Spalte "CPU" kleiner als 5, wird sie grün gefärbt. Zwischen 10 und 50 wird sie gelb und wenn der Wert größer als 50 ist, wird sie rot. Die Reihenfolge der Spalten wird angepasst. .EXAMPLE $Sample1 = Get-Process | select -First 10 -Property name,ws,cpu $Sample2 = Get-service | select -First 10 -Property name,Status,DisplayName $HTML = erstelle-HTMLReport ` -Daten $Sample1 ` -Headline 'Prozessliste' ` -Sortierung 'name','ws','cpu' ` -gelb '$_.cpu -gt 10' ` -gruen '$_.cpu -lt 5' ` -rot '$_.cpu -gt 50' $HTML = erstelle-HTMLReport ` -HTML $HTML ` -Daten $Sample2 ` -Headline 'Serviceliste' ` -Sortierung 'name','Status','DisplayName' ` -gelb '$_.Status -eq "stopped"' ` -gruen '$_.Status -eq "running"' $HTML | Out-File -FilePath "$($env:USERPROFILE)\Desktop\Sample.htm" & "$($env:USERPROFILE)\Desktop\Sample.htm" Die erste Verwendung der Funktion erstellt einen fertigen HTML-Text aus der Tabelle $Sample1. Der Text wird im zweiten Aufruf als Argument an den Parameter -HTML übergeben. Dadurch wird die Tabelle $Sample2 in den HTML-Text integriert. Der finale Text wird mit out-file gespeichert und im Browser geöffnet. .EXAMPLE Get-Process | erstelle-HTMLReport -Dateiname "$($env:USERPROFILE)\Desktop\Sample.htm" & "$($env:USERPROFILE)\Desktop\Sample.htm" Hier wird die Funktion über die Pipeline aufgerufen. .PARAMETER Daten An diesen Parameter wird die zweidimensionale Datentabelle übergeben. Sie kann aber auch durch die Pipeline referenziert werden. Der Parameter ist pflicht. .PARAMETER HTML Wenn mehrere Tabellen in einem gemeinsamen HTML-Text untergebracht werden sollen, dann kann die Funktion erneut aufgerufen werden. Der Text aus dem ersten Aufruf wird dann über den Parameter HTML übergeben. So werden mehrfache Header vermieden. Wenn nur eine Tabelle gerendert werden soll, dann kann das Argument leer bleiben. .PARAMETER Muster Mit diesem Schalter wird eine abwechselnde Hintergrundfarbe der Tabellenzeilen aktiviert. Das erhöht die Lesbarkeit. Der Parameter schließt die gleichzeitige Verwendung der anderen Farbparameter aus. .PARAMETER gruen Der Farbparameter kann nur ohne den Parameter -Muster verwendet werden. Kombinationen mit den anderen Farbparametern sind möglich. Es muss als Argument eine Bedingung formuliert werden. Die Syntax entspricht der eines Where-Object. Ist die Bedingung erfüllt, dann wird die Tabellenzeile die angegebene Parameterfarbe als Hintergrundfarbe erhalten. get-process | where-object {$_.ws -gt 100MB} # ==> liefert alle Prozesse mit mehr als 100MB RAM get-process | erstelle-HTML -rot '$_.ws -gt 100MB' # ==> in der HTML-Tabelle werden alle Prozesszeilen mit mehr als 100MB RAM rot gefärbt .PARAMETER gelb Der Farbparameter kann nur ohne den Parameter -Muster verwendet werden. Kombinationen mit den anderen Farbparametern sind möglich. Es muss als Argument eine Bedingung formuliert werden. Die Syntax entspricht der eines Where-Object. Ist die Bedingung erfüllt, dann wird die Tabellenzeile die angegebene Parameterfarbe als Hintergrundfarbe erhalten. get-process | where-object {$_.ws -gt 100MB} # ==> liefert alle Prozesse mit mehr als 100MB RAM get-process | erstelle-HTML -rot '$_.ws -gt 100MB' # ==> in der HTML-Tabelle werden alle Prozesszeilen mit mehr als 100MB RAM rot gefärbt .PARAMETER rot Der Farbparameter kann nur ohne den Parameter -Muster verwendet werden. Kombinationen mit den anderen Farbparametern sind möglich. Es muss als Argument eine Bedingung formuliert werden. Die Syntax entspricht der eines Where-Object. Ist die Bedingung erfüllt, dann wird die Tabellenzeile die angegebene Parameterfarbe als Hintergrundfarbe erhalten. get-process | where-object {$_.ws -gt 100MB} # ==> liefert alle Prozesse mit mehr als 100MB RAM get-process | erstelle-HTML -rot '$_.ws -gt 100MB' # ==> in der HTML-Tabelle werden alle Prozesszeilen mit mehr als 100MB RAM rot gefärbt .PARAMETER blau Der Farbparameter kann nur ohne den Parameter -Muster verwendet werden. Kombinationen mit den anderen Farbparametern sind möglich. Es muss als Argument eine Bedingung formuliert werden. Die Syntax entspricht der eines Where-Object. Ist die Bedingung erfüllt, dann wird die Tabellenzeile die angegebene Parameterfarbe als Hintergrundfarbe erhalten. get-process | where-object {$_.ws -gt 100MB} # ==> liefert alle Prozesse mit mehr als 100MB RAM get-process | erstelle-HTML -rot '$_.ws -gt 100MB' # ==> in der HTML-Tabelle werden alle Prozesszeilen mit mehr als 100MB RAM rot gefärbt .PARAMETER Sortierung Ohne den Parameter -Sortierung werden die Tabellenspalten mit einem get-member automatisch ermittelt. Dabei kann aber die Sortierung verändert werden. Stattdessen können die Namen der Spalten in der richtigen Reihenfolge als Argument angegeben werden. Damit können auch Spalten ausgelassen werden. .PARAMETER Dateiname Ohne den Parameter -Dateiname werden die HTML-Daten als Text zurückgegeben. Alternativ kann auch ein Dateipfad als Argument übergeben werden. Dann speichert die Funktion den HTML-Text direkt in der Datei. #> param( [cmdletbinding()] [parameter(Mandatory=$true,ValueFromPipeline=$true)] [Object[]] $Daten = @(), [parameter(Mandatory=$false)] [string[]] $HTML = @(), [parameter(Mandatory=$false)] [string] $Headline = '', [parameter(Mandatory=$false)] [Switch] $Muster = $false, [parameter(Mandatory=$false)] [string] $gruen = '', [parameter(Mandatory=$false)] [string] $gelb = '', [parameter(Mandatory=$false)] [string] $rot = '', [parameter(Mandatory=$false)] [string] $blau = '', [parameter(Mandatory=$false)] [string[]] $Sortierung = '', [parameter(Mandatory=$false)] [string] $Dateiname = '' ) begin { #region Variablen $Ausgabe = @() $Tabelle = @() #endregion #region erzeuge HTML-Header if ($HTML.count -eq 0) { $Ausgabe += "<HTML>" $Ausgabe += "<STYLE>" $Ausgabe += " BODY{font-family: Arial; font-size: 8pt;}" $Ausgabe += " H1{font-size: 16px;}" $Ausgabe += " H2{font-size: 14px;}" $Ausgabe += " H3{font-size: 12px;}" $Ausgabe += " TABLE{border: 1px solid black; border-collapse: collapse; font-size: 8pt;}" $Ausgabe += " TH{border: 1px solid black; background: #dddddd; padding: 5px; color: #000000;}" $Ausgabe += " TD{border: 1px solid black; padding: 5px; }" $Ausgabe += " td.pass{background: #7FFF00;}" $Ausgabe += " td.warn{background: #FFE600;}" $Ausgabe += " td.fail{background: #FF0000; color: #ffffff;} " $Ausgabe += " td.info{background: #85D4FF;}" $Ausgabe += " td.inf2{background: #B2F0FF;}" $Ausgabe += "</STYLE>" $Ausgabe += "<BODY>" } else { $Ausgabe += $HTML -split "`r`n" | Select-Object -SkipLast 1 } #endregion #region erzeuge Headline if ($Headline -ne '') {$Ausgabe += "<h3>$Headline</h3>"} #endregion } process { #region Daten sammeln $Tabelle += $Daten #endregion } end { #region generiere Kopfzeile der Ausgabetabelle $Spaltennamen = "" if (($Tabelle).length -ne 0) { if ($Sortierung[0] -eq '') { $Spaltennamen = ($Tabelle | Get-Member -MemberType Properties | Select-Object -ExpandProperty name) -join '</TH><TH align="left">' } else { $Spaltennamen = $Sortierung -join '</TH><TH align="left">' } } #endregion #region erzeuge Zeilen der Ausgabetabelle if (($Tabelle).length -ne 0) { $Ausgabe += " <TABLE>" $Ausgabe += ' <TH align="left">' + $Spaltennamen + "</TH>" $Tabelle | ForEach-Object { $DatenZeile = $_ $Tabellenzeile = ' <TR>' $stat = 'leer' if ($Muster) { if ($FarbeAn) { $stat = "inf2" $FarbeAn = $false } else { $stat = "leer" $FarbeAn = $true } } else { if ($blau -ne '') { Invoke-Expression ('if (' + $blau + ' ) {$stat = "info"}') } if ($gruen -ne '') { Invoke-Expression ('if (' + $gruen + ' ) {$stat = "pass"}') } if ($gelb -ne '') { Invoke-Expression ('if (' + $gelb + ' ) {$stat = "warn"}') } if ($rot -ne '') { Invoke-Expression ('if (' + $rot + ' ) {$stat = "fail"}') } } if ($Sortierung[0] -eq '') { $Tabelle | Get-Member -MemberType Properties | ForEach-Object { $Spalte = $_.Name $Tabellenzeile = $Tabellenzeile + '<TD class="' + $stat + '">' + $DatenZeile.$Spalte + '</TD>' } } else { $Sortierung | ForEach-Object { $Spalte = $_ $Tabellenzeile = $Tabellenzeile + '<TD class="' + $stat + '">' + $DatenZeile.$Spalte + '</TD>' } } $Ausgabe += $Tabellenzeile + '</TR>' } $Ausgabe += ' </TABLE>' } $Ausgabe += '</BODY></HTML>' #endregion #region Ausgabe if ($Dateiname -eq '') { RETURN ($Ausgabe -join "`r`n") } else { ($Ausgabe -join "`r`n") | Out-File -FilePath $Dateiname -Encoding default } #endregion } }
Und so könnten die Szenarien der Verwendung aussehen. Hier übergebe ich ein paar Zeilen aus der Prozessliste an die Funktion. Mit dem Parameter -Muster generiere ich abwechselnde Hintergrundfarben für die Zeilen. Der Text wird als Ergebnis ausgegeben:
Im nächsten Beispiel verwende ich Bedingungen für die gezielte Einfärbung der Zeilen. Die Syntax entspricht dabei der vom Cmdlet Where-Object:
Das dritte Beispiel zeigt die Verbindung von zwei PowerShell-Tabellen in einem HTML-Text. Das Ergebnis des ersten Aufrufs wird in der Variable $HTML gespeichert und beim zweiten Aufruf als Argument übergeben. Die zweite Tabelle wird dabei an die richtige Stelle im HTML-Text eingebettet:
Mein 4. Beispiel zeigt die Pipeline-Fähigkeit. Ebenso kann natürlich auch auf die Einfärbung verzichtet werden:
Die Funktion habe ich bereits in vielen meiner Scripte verbaut. So spare ich mir viel Zeit beim Lesen der unzähligen Logmails pro Tag. Und etwas Farbe kann man auch so immer gut gebrauchen.
Das Script mit Beispielen könnt ihr hier als zip-Archiv herunterladen.
Stay tuned!