powershell脚本格式表输出中的颜色词

时间:2011-09-09 13:09:29

标签: powershell powershell-v2.0

是否可以使用format-table为powershell输出仅为某些单词(不是完整的行)着色。例如,此脚本以递归方式扫描文件夹中的字符串,然后使用format-table输出结果。

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

能够使用特定颜色格式化我们正在搜索的单词会很好,这样您就可以准确地看到它在线上的位置。

4 个答案:

答案 0 :(得分:14)

您可以将表格导入Out-String,然后使用带有Write-Host开关的-NoNewLine将字符串分成几部分。

这样的事情:

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

答案 1 :(得分:5)

我喜欢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!

答案 2 :(得分: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'

答案 3 :(得分:1)

#$VerbosePreference = 'continue'
$VerbosePreference = 'silent'

filter ColorPattern {
    param ([object]$colors, [switch]$SimpleMatch)
    [string]$line = $_

    $collection = New-Object 'System.Collections.Generic.SortedDictionary[int, pscustomobject]'
    $RegexOptions = [Text.RegularExpressions.RegexOptions]::IgnoreCase.value__ + [Text.RegularExpressions.RegexOptions]::Singleline.value__

    if ($SimpleMatch){
        $patternMatches = $colors.keys | % {[regex]::Escape($_)}
        $reference = 'Value'
    } else {
        $patternMatches = $colors.keys
        $reference = 'Pattern'
    }

    # detect RegEx matches and add to collection object
    Write-Verbose "'$line'"

    $measureparsing_match = (Measure-Command {
        foreach ($pattern in $patternMatches){
            Write-Verbose "regex pattern: $pattern"
            foreach ($match in ([regex]::Matches($line, $pattern, $RegexOptions))){ # lazy matching
                Write-Verbose "`tmatch index: $($match.Index) length: $($match.length)"

                $currentset = ($match.Index)..($match.Index + $match.length - 1)
                Write-Verbose "`tcurrent set: $currentset"

                if (-not [bool]$collection.Count){
                    Write-Verbose "`t`tindex: $($match.Index) value: $($match.value) (inital add)"
                    [void]$collection.Add($match.Index, [PSCustomObject]@{Length = $match.Length; Value = $match.Value; Pattern = $pattern; Range = $currentset})
                } else {
                    (,$collection.Values) | % {
                        $currentRange = $_.range
                        $intersect = Compare-Object -PassThru $currentset $currentRange -IncludeEqual -ExcludeDifferent
                        if ($intersect){
                            Write-Verbose "`t`tintersect: $([string]($intersect | % {[string]::Concat($_)})) (skipped)"

                            $nonintersect = Compare-Object -PassThru $currentset $intersect
                            Write-Verbose "`t`tnonintersect: $([string]($nonintersect | % {[string]::Concat($_)}))"

                            $nonintersect | % {
                                if ($currentRange -notcontains $_){
                                    Write-Verbose "`t`tindex: $_ value: $($line[$_]) (adding intersect-fallout)"
                                    [void]$collection.Add($_, [PSCustomObject]@{Length = $_.Length; Value = $line[$_]; Pattern = $pattern; Range = $currentset})
                                } else {
                                    Write-Verbose "`t`t`tindex: $_ value: $($line[$_]) (skipped intersect-fallout)"
                                }
                            }
                        } else {
                            Write-Verbose "`t`tindex: $($match.index) value: $($match.value) (adding nonintersect)"
                            [void]$collection.Add($match.Index, [PSCustomObject]@{Length = $match.Length; Value = $match.Value; Pattern = $pattern; Range = $currentset})
                        }
                    } # end values
                } #end if
            } # end matching
        } # end pattern
    }).TotalMilliseconds

    $measureparsing_nonmatch = (Measure-Command {
        if ([bool]$collection.count){ # if there are no matches, skip!
            Compare-Object -PassThru `
            -ReferenceObject (
                $collection.Keys | % { # all matched keys and their lengths
                    $word = $collection.item($_)
                    $currentlength = ($word.value).length
                    ($_..($_ + ($currentlength - 1)))
                }) `
            -DifferenceObject (0..($line.Length - 1)) | # entire line
                % {[void]$collection.Add($_, [PSCustomObject]@{Length = $_.length; Value = $line[$_]})} # add non matches to collection
        }
    }).TotalMilliseconds

    Write-Verbose "match: $measureparsing_match ms. VS nonmatch: $measureparsing_nonmatch ms."

    $collection.keys | % {
        $word = $collection.item($_)
        if ($word.pattern){
            if ($colors.ContainsKey($word.$reference)){
                $color = @{
                    ForegroundColor = $colors[$word.$reference].ForegroundColor;
                    BackgroundColor = $colors[$word.$reference].BackgroundColor
                }
                if ($word.value){
                    Write-Host -NoNewline $([string]::Concat($word.value)) @color
                }
            }
        } else {
            Write-Host -NoNewline $([string]::Concat($word.value))
        }
    }
    Write-Host # needed for line feed
}

$Patterns = [ordered]@{
    # higher in list takes precendence
    'stopped' = @{ForegroundColor = 'Red'; BackgroundColor='DarkRed'}
    'running' = @{ForegroundColor = 'Green'; BackgroundColor='DarkGreen'}
    'paused' = @{ForegroundColor = 'Yellow'; BackgroundColor='DarkYellow'}
    0 = @{ForegroundColor = 'White'; BackgroundColor='Gray'}
    '\d+' = @{ForegroundColor = 'Gray'; BackgroundColor='Black'}
    '\.' = @{ForegroundColor = 'Magenta'; BackgroundColor='DarkMagenta'}
    '(a|e|i|o|u)' = @{ForegroundColor = 'Yellow'; BackgroundColor='DarkYellow'}
    '\w+' = @{ForegroundColor = 'Cyan'; BackgroundColor='DarkCyan'}

}

# strongly typed collection.. we could probably do this better..
$colorCollection = New-Object 'system.collections.generic.dictionary[string, hashtable]'([StringComparer]::OrdinalIgnoreCase) # Ordinal
$Patterns.GetEnumerator() | % {[void]$colorCollection.Add($_.Name, $_.Value)}

Get-Service | Out-String -Stream | ColorPattern -colors $colorCollection
#Get-Service | Out-String -Stream | ColorPattern -colors $colorCollection -SimpleMatch

回复迟到了,但我已经通过多个正则表达式支持以及简单匹配更新了这个。这是在Powershell v4.0下测试的。