Colored words in powershell script file format table

Is it possible to color only certain words (not complete lines) for powershell output using a format table. For example, this script recursively scans a folder for a row, and then displays the result using a format table.

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

It would be nice to format the search term with a specific color so that you can see exactly where it was found in the line.

+7
source share
4 answers

You can connect the table to Out-String , and then write the string in parts using Write-Host using the -NoNewLine switch.

Something like that:

 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 
+14
source

I like Rynant . Here is an alternative implementation using -split instead of 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 includes empty lines if the line starts or ends with the given word, therefore, the additional logic is "if not the first".


Edit: Following Rynant's comment, here is another implementation that supports both simple and regular patterns:

 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 } 

The result from the following examples shows the difference:

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

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

+5
source

I love the answer @Ryant gave. I have a modified version here that can be used to color multiple words in the output by passing arrays or words and colors. The trick is that you need to split the input text into lines based on the newline separator.

 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 } } 

and will be executed as follows

 Get-Service | Format-Table| Out-String| ColorWord2 -word 'Running','Stopped' -color 'Green','Red' 
+3
source
 #$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 

Late to the answer, but I updated this with multiple regex support, as well as simple matching. This has been tested in Powershell v4.0.

+1
source

All Articles