Tecnica per la formattazione selettiva dei dati in una pipeline PowerShell e output in formato HTML

Supponete di voler eseguire una formattazione elaborata di alcuni output tabulari da PowerShell e la destinazione deve essere html (o per un server Web o per essere inviata in un messaggio di posta elettronica). Supponiamo ad esempio che desideri che determinati valori numerici abbiano un diverso colore di sfondo. Qualunque cosa. Posso pensare a due modi programmatici solidi per ottenere questo risultato: output XML e trasformazione con XSLT o output HTML e decorazione con CSS.

XSLT è probabilmente il più difficile dei due (lo dico perché non lo so), ma da quel poco che ricordo, ha il vantaggio di portare in grado di incorporare i criteri di selezione (xpath?) Per la formattazione di fantasia sopra menzionata. I CSS d’altra parte hanno bisogno di una mano. Se si desidera che una determinata cella venga trattata in modo specifico, sarà necessario distinguerla dai suoi fratelli con una class, un ID o qualcosa del genere. PowerShell non ha un modo per farlo in modo nativo, quindi significherebbe analizzare l’HTML mentre lascia convertto-html e aggiungendo, ad esempio, una class “emphasis”:

32MB 

Non mi piace l’idea dell’analisi del testo richiesta, soprattutto considerando che preferirei in qualche modo sottolineare ciò che è necessario enfatizzare in Powershell prima che colpisca l’HTML.

XSLT è il modo migliore? Hai suggerimenti su come contrassegnare l’HTML dopo che lascia convertto-html o idee di un modo diverso?

Un modo molto più veloce:

Ok, continuo a promettermi che non passerò più tempo a risolvere i problemi, ma … l’affermazione dell’interruttore nella mia seconda risposta ha richiesto più di 10 secondi per funzionare sul mio sistema, perché sta facendo il “dove” in PowerShell anziché in LINQ.

Poiché PowerShell non supporta LINQ, l’ho risolto scrivendo un metodo di supporto statico in una chiamata di tipo Aggiungi (e velocizzato quella dichiarazione di commutazione di circa 1000x):

 Add-Type -Language CSharpVersion3 -ReferencedAssemblies System.Xml, System.Xml.Linq -UsingNamespace System.Linq -Name XUtilities -Namespace Huddled -MemberDefinition @" public static System.Collections.Generic.IEnumerable GetElementByIndex( System.Xml.Linq.XContainer doc, System.Xml.Linq.XName element, int index) { return from e in doc.Descendants(element) where e.NodesBeforeSelf().Count() == index select e; } public static System.Collections.Generic.IEnumerable GetElementByValue( System.Xml.Linq.XContainer doc, System.Xml.Linq.XName element, string value) { return from e in doc.Descendants(element) where e.Value == value select e; } "@ # Get the running processes to x(ht)ml $xml = [System.Xml.Linq.XDocument]::Parse( "$(Get-Process | ConvertTo-Html)" ) # Find the index of the column you want to format: $wsIndex = [Huddled.XUtilities]::GetElementByValue( $xml, "{http://www.w3.org/1999/xhtml}th", "WS" ) | %{ ($_.NodesBeforeSelf() | Measure).Count } switch([Huddled.XUtilities]::GetElementByIndex( $xml, "{http://www.w3.org/1999/xhtml}td", $wsIndex )) { {200MB -lt $_.Value } { $_.SetAttributeValue( "style", "background: red;"); continue } {20MB -lt $_.Value } { $_.SetAttributeValue( "style", "background: orange;"); continue } {10MB -lt $_.Value } { $_.SetAttributeValue( "style", "background: yellow;"); continue } } # Save the html out to a file $xml.Save("$pwd/procs2.html") # Open the thing in your browser to see what we've wrought ii .\procs2.html 

PowerShell 3:

Ho ridimensionato questo in PowerShell 3 dopo che qualcuno si è collegato a questo post e non hai più bisogno dei tipi compilati per farlo in fretta:

 Add-Type -AssemblyName System.Xml.Linq $Process = $(Get-Process | Select Handles, NPM, PM, WS, VM, CPU, Id, ProcessName) $xml = [System.Xml.Linq.XDocument]::Parse( "$($Process | ConvertTo-Html)" ) if($Namespace = $xml.Root.Attribute("xmlns").Value) { $Namespace = "{{{0}}}" -f $Namespace } # Find the index of the column you want to format: $wsIndex = [Array]::IndexOf( $xml.Descendants("${Namespace}th").Value, "WS") foreach($row in $xml.Descendants("${Namespace}tr")){ switch(@($row.Descendants("${Namespace}td"))[$wsIndex]) { {200MB -lt $_.Value } { $_.SetAttributeValue( "style", "background: red;"); continue } {20MB -lt $_.Value } { $_.SetAttributeValue( "style", "background: orange;"); continue } {10MB -lt $_.Value } { $_.SetAttributeValue( "style", "background: yellow;"); continue } } } # Save the html out to a file $xml.Save("$pwd/procs1.html") # Open the thing in your browser to see what we've wrought ii .\procs2.html 

Che ne dici di usare JQuery e inserire un’intestazione con lo script JQuery e alcuni stili, come:

 Get-Process | ConvertTo-Html -Head @'   '@ | Out-File procs.html; ii .\procs.html 

Ehi, ho trovato un’altra risposta che mi piace di più. Questo non si basa sul browser che supporta JavaScript …

 Add-Type -AssemblyName System.Xml.Linq # Get the running processes to x(ht)ml $xml = [System.Xml.Linq.XDocument]::Parse( "$(Get-Process | ConvertTo-Html)" ) # Find the index of the column you want to format: $wsIndex = (($xml.Descendants("{http://www.w3.org/1999/xhtml}th") | Where-Object { $_.Value -eq "WS" }).NodesBeforeSelf() | Measure-Object).Count # Format the column based on whatever rules you have: switch($xml.Descendants("{http://www.w3.org/1999/xhtml}td") | Where { ($_.NodesBeforeSelf() | Measure).Count -eq $wsIndex } ) { {200MB -lt $_.Value } { $_.SetAttributeValue( "style", "background: red;"); continue } {20MB -lt $_.Value } { $_.SetAttributeValue( "style", "background: orange;"); continue } {10MB -lt $_.Value } { $_.SetAttributeValue( "style", "background: yellow;"); continue } } # Save the html out to a file $xml.Save("$pwd/procs2.html") # Open the thing in your browser to see what we've wrought ii .\procs2.html 

C’è un distintivo speciale per rubare il “segnato come risposta” da te stesso? 😉

Come risultato di alcune discussioni su github, ho aggiornato questo per PowerShell 4/5/6 utilizzando il metodo .where() , eliminando così la dipendenza dalla pipeline. La parte lenta sta generando l’HTML mentre la manipolazione XML effettiva richiede solo ~ 200 ms.

 Add-Type -AssemblyName System.Xml.Linq # Get the running processes to x(ht)ml. This is *SLOW*. $xml = [System.Xml.Linq.XDocument]::Parse([string] (Get-Process | ConvertTo-Html)) # Find the index of the column you want to format: $wsIndex = $xml.Descendants("{http://www.w3.org/1999/xhtml}th"). Where{$_.Value -eq "WS" }. NodesBeforeSelf(). Count # Format the column based on whatever rules you have: switch($xml.Descendants("{http://www.w3.org/1999/xhtml}td").Where{@($_.NodesBeforeSelf()).Count -eq $wsIndex} ) { {200MB -lt $_.Value } { $_.SetAttributeValue( "style", "background: red;"); continue } {20MB -lt $_.Value } { $_.SetAttributeValue( "style", "background: orange;"); continue } {10MB -lt $_.Value } { $_.SetAttributeValue( "style", "background: yellow;"); continue } } # Save the html out to a file $xml.Save("$pwd/procs2.html") # Open the thing in your browser to see what we've wrought ii .\procs2.html