有选择地格式化 PowerShell 管道中的数据并输出为 HTML 的技术

发布于 2024-10-09 15:51:05 字数 571 浏览 0 评论 0原文

假设您想要对 powershell 的某些表格输出进行一些奇特的格式化,并且目标是 html(用于网络服务器,或者通过电子邮件发送)。举例来说,您希望某些数值具有不同的背景颜色。任何。我可以想到两种可靠的编程方法来完成此任务:输出 XML 并使用 XSLT 进行转换,或者输出 HTML 并使用 CSS 进行装饰。

XSLT 可能是两者中较难的一个(我这么说是因为我不知道),但据我所知,它的好处是能够嵌入上述花哨格式的选择标准(xpath?)。另一方面,CSS 需要帮助。如果您希望对某个单元进行特殊处理,那么您需要使用类、id 或类似的内容将其与其兄弟姐妹区分开来。 PowerShell 本身并没有办法做到这一点,因此这意味着在离开 Convertto-html 时解析 HTML 并添加例如“强调”类:

<td class="emphasis">32MB</td>

我不喜欢所需的文本解析的想法,特别是考虑到我宁愿能够在 Powershell 中强调 HTML 之前强调它。

XSLT 是最好的方法吗?对于如何在离开 Convertto-html 后标记 HTML 有建议或不同方式的想法吗?

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's say for example that you want certain numeric values to have a different background color. Whatever. I can think of two solid programmatic ways to accomplish this: output XML and transform with XSLT, or output HTML and decorate with CSS.

XSLT is probably the harder of the two (I say that because I don't know it), but from what little I recall, it has the benefit of bring able to embed the selection criteria (xpath?) for aforementioned fancy formatting. CSS on the other hand needs a helping hand. If you wanted a certain cell to be treated specially, then you would need to distinguish it from its siblings with a class, id, or something along those lines. PowerShell doesn't really have a way to do that natively, so that would mean parsing the HTML as it leaves convertto-html and adding, for example, a "emphasis" class:

<td class="emphasis">32MB</td>

I don't like the idea of the required text parsing, especially given that I would rather be able to somehow emphasize what needs emphasizing in Powershell before it hits HTML.

Is XSLT the best way? Have suggestions for how to markup the HTML after it leaves convertto-html or ideas of a different way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

檐上三寸雪 2024-10-16 15:51:05

嘿,我想出了另一个我更喜欢的答案。这个不依赖于支持 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

是否有一个特殊的徽章可以窃取您自己的“标记为答案”? ;-)

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? ;-)

懷念過去 2024-10-16 15:51:05

如何使用 JQuery,并使用 JQUery 脚本和一些样式插入标头,例如:

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

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
时光是把杀猪刀 2024-10-16 15:51:05

更快的方法:

好的,我一直向自己保证,我不会再花时间解决问题,但是......我的第二个答案中的 switch 语句在我的机器上运行需要 10 多秒system,--因为它是在 PowerShell 中而不是 LINQ 中执行“where”操作。

由于 PowerShell 不支持 LINQ,我通过在 Add-Type 调用中编写一个静态帮助器方法来解决这个问题(并将 switch 语句的速度加快了约 1000 倍):

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:

在有人链接到这篇文章后,我在 PowerShell 3 中重新做了这个,并且您不再需要编译类型来快速获取它:

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

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
一枫情书 2024-10-16 15:51:05

经过一些 github 讨论的结果,我使用 .where() 方法针对 PowerShell 4/5/6 进行了更新,从而消除了对管道的依赖。缓慢的部分是生成 HTML,而实际的 XML 操作只需要大约 200 毫秒。

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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文