Technique for selectively formatting data in a PowerShell pipeline and output as HTML

前端 未结 4 1700
一向
一向 2021-02-02 04:25

Say that you want to do some fancy formatting of some tabular output from powershell, and the destination is to be html (either for a webserver, or to be sent in an email). Let\

相关标签:
4条回答
  • 2021-02-02 05:20

    Hey, I have come up with another answer which I like better. This one doesn't rely on the browser supporting 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
    

    Is there a special badge for stealing the "marked as answer" from yourself? ;-)

    0 讨论(0)
  • 2021-02-02 05:21

    A much faster way:

    Ok, I keep promising myself that I won't spend time on solved problems anymore, but ... that switch statement in my second answer was taking over 10 seconds to run on my system, -- because it's doing the "where" stuff in PowerShell instead of in LINQ.

    Since PowerShell doesn't support LINQ, I solved it by writing a static helper method in an Add-Type call (and sped up that switch statement by about 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<System.Xml.Linq.XElement> 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<System.Xml.Linq.XElement> 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:

    I redid this in PowerShell 3 after someone linked to this post, and you no longer need the compiled types to get it fast:

    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
    
    0 讨论(0)
  • 2021-02-02 05:28

    As a result of some github discussions, I've updated this for PowerShell 4/5/6 using the .where() method thus eliminating the dependency on the pipeline. The slow part is generating the HTML whereas the actual XML manipulation only takes ~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
    
    0 讨论(0)
  • 2021-02-02 05:31

    How about using JQuery, and inserting a header with the JQUery script and some styling, like:

    Get-Process | ConvertTo-Html -Head @'
    
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
    <script type="text/javascript">
    $(document).ready(function() {
    
       $("table tr td:nth-child(" + ( $("table th:contains('WS')").index() + 1 ) + ")").each(function() {
          if($(this).html() > 209715200) { // 200MB
             $(this).css("background", "red" );
          } else if($(this).html() > 20971520) { // 20MB
             $(this).css("background", "orange" );
          } else if($(this).html() > 10485760) { // 10MB
             $(this).css("background", "yellow" );
          }
       });
    
    })
    </script>
    
    '@ | Out-File procs.html; ii .\procs.html
    
    0 讨论(0)
提交回复
热议问题