在 PowerShell 脚本的 format-table 输出中着色词汇

8

使用format-table在powershell输出结果时,是否可以仅对某些单词(而不是整行)进行着色。例如,此脚本递归扫描文件夹以查找字符串,然后使用format-table输出结果。

dir -r -i *.* | Select-String $args[0] |
format-table -Property @{label="Line #"; Expression={$_.LineNumber}; width=6},
Path, Line -wrap

希望能够用特定颜色格式化我们正在搜索的单词,这样你就可以清楚地看到它在行中的位置。

3个回答

16
你可以使用管道将表格传输到 Out-String,然后使用带有 -NoNewLine 开关的 Write-Host 逐部分写入字符串。
类似这样的代码:
filter ColorWord {
    param(
        [string] $word,
        [string] $color
    )
    $line = $_
    $index = $line.IndexOf($word, [System.StringComparison]::InvariantCultureIgnoreCase)
    while($index -ge 0){
        Write-Host $line.Substring(0,$index) -NoNewline
        Write-Host $line.Substring($index, $word.Length) -NoNewline -ForegroundColor $color
        $used = $word.Length + $index
        $remain = $line.Length - $used
        $line = $line.Substring($used, $remain)
        $index = $line.IndexOf($word, [System.StringComparison]::InvariantCultureIgnoreCase)
    }
    Write-Host $line
}

Get-Process| Format-Table| Out-String| ColorWord -word 1 -color magenta

2
非常好!修改了这一行代码:$index = $line.IndexOf($word, [System.StringComparison]::InvariantCultureIgnoreCase) - EtienneT
忽略大小写是个好点子。我已经更新了答案并进行了修改。 - Rynant

6
我喜欢 Rynant 的方法。这里是另一种实现方式,使用 -split 而不是 IndexOf
filter ColorWord( [string]$word, [ConsoleColor]$color ) {
    $later = $false
    $_ -split [regex]::Escape( $word ) | foreach {
      if( $later ) { Write-Host "$word" -NoNewline -ForegroundColor $color }
      else { $later = $true }
      Write-Host $_ -NoNewline
    }
    Write-Host
}

如果一行以给定的单词开头或结尾,Split 函数会包含空字符串,因此需要额外的“if not first”逻辑。

编辑:根据Rynant的评论,这里提供另一种实现方式,支持简单和正则表达式模式:

filter ColorPattern( [string]$Pattern, [ConsoleColor]$Color, [switch]$SimpleMatch ) {
  if( $SimpleMatch ) { $Pattern = [regex]::Escape( $Pattern ) }
  
  $split = $_ -split $Pattern
  $found = [regex]::Matches( $_, $Pattern, 'IgnoreCase' )
  for( $i = 0; $i -lt $split.Count; ++$i ) {
    Write-Host $split[$i] -NoNewline
    Write-Host $found[$i] -NoNewline -ForegroundColor $Color
  }
  
  Write-Host
}

以下示例的输出显示了它们之间的区别:

PS> '\d00\d!' | ColorPattern '\d' 'Magenta' -Simple
\d00\d!

PS> '\d00\d!' | ColorPattern '\d' 'Magenta'
\d00\d!


1
请注意需要转义正则表达式字符。例如:'Ke$ha - Tik Tok.mp3'| ColorWords 'ke\$ha' red。或者在过滤器中可以使用 $_ -split [regex]::Escape($word)。除非您想要使用正则表达式进行匹配,这可能会很好。 - Rynant
@Rynant 感谢您指出这一点!已更新为新版本。 - Emperor XLII

4

我喜欢@Ryant提供的答案。这里有一个修改过的版本,可以通过传递数组或单词和颜色来对输出中的多个单词进行着色。诀窍是您必须根据换行符将输入文本拆分成行。

filter ColorWord2 {
param(
    [string[]] $word,
    [string[]] $color
)
$all = $_
$lines = ($_ -split '\r\n')

$lines | % {
    $line = $_      
    $x = -1

    $word | % {
        $x++
        $item = $_      

        $index = $line.IndexOf($item, [System.StringComparison]::InvariantCultureIgnoreCase)                            
            while($index -ge 0){
                Write-Host $line.Substring(0,$index) -NoNewline                 
                Write-Host $line.Substring($index, $item.Length) -NoNewline -ForegroundColor $color[$x]
                $used =$item.Length + $index
                $remain = $line.Length - $used
                $line =$line.Substring($used, $remain)
                $index = $line.IndexOf($item, [System.StringComparison]::InvariantCultureIgnoreCase)
            }
        }

    Write-Host $line
} }

并将按以下方式执行

Get-Service | Format-Table| Out-String| ColorWord2 -word 'Running','Stopped' -color 'Green','Red'

我喜欢这个答案,这正是我在寻找的。 - sch4v4
干杯。我现在实际上使用这个的变体。与其传递两个数组,我只传递一个哈希表,其中颜色是键,值是将应用颜色的单词数组。 - stevethethread

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接