This is a question of technique, but as an exercise my intention is to write a PS to accept piped input, with a regex as a parameter, and highlight any text matching the regex.
The part I'm not able to find any info on is that it's easy to match text, capturing to a buffer, or to replace text. But I need to replace matched text with color control, the original text, then resume the previous color. I can't seem to find any way to generate color output other than with write-output, and can't do separate colors in a single write, which would mean:
-matching the regex
-write-host out all text prior to the match in default color, with -NoNewLine
-write-host the match, with -NoNewLine
-write-host the remainder
This seems messy, and gets even more messy if we want to support multiple matches. Is there a more eloquent way to do this?
Write-Host
is the right way to do this. Use the .Index
and .Length
properties of the resulting Match
object to determine where exactly the matched text is. You just need to be a bit careful keeping track of indices :)
This works for multiple matches, and is not terribly untidy IMO:
function ColorMatch
{
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string] $InputObject,
[Parameter(Mandatory = $true, Position = 0)]
[string] $Pattern
)
begin{ $r = [regex]$Pattern }
process
{
$ms = $r.Matches($inputObject)
$startIndex = 0
foreach($m in $ms)
{
$nonMatchLength = $m.Index - $startIndex
Write-Host $inputObject.Substring($startIndex, $nonMatchLength) -NoNew
Write-Host $m.Value -Back DarkRed -NoNew
$startIndex = $m.Index + $m.Length
}
if($startIndex -lt $inputObject.Length)
{
Write-Host $inputObject.Substring($startIndex) -NoNew
}
Write-Host
}
}
This is an extension of latkin's answer. Here I'm extending the Match object such that it can be processed for this purpose - and others - more easily.
function Split-Match {
param([Parameter(Mandatory = $true)]
$match
)
$sections = @()
$start = 0
$text = $m.Line
foreach ($m in $match.Matches) {
$i = $m.Index
$l = $m.Length
$sections += $false, $text.Substring($start, $i - $start)
$sections += $true, $text.Substring($i, $l)
$start = $i + $l
}
$sections += $false, $text.Substring($start)
$match | Add-Member -Force Sections $sections
$match
}
function Write-Match {
param([Parameter(Mandatory = $true)]
$match
)
$fg = "White"
$bg = "Black"
foreach($s in $match.Sections) {
if ($s.GetType() -eq [bool]) {
if ($s) {
$fg = "White"
$bg = "Red"
} else {
$fg = "White"
$bg = "Black"
}
} else {
Write-Host -NoNewline -ForegroundColor $fg -BackgroundColor $bg $s
}
}
Write-Host
}
$string = @'
Match this A
Not this B
Not this C
But this A
'@
$m = $string | select-string -CaseSensitive -AllMatches "A"
$m = Split-Match $m
Write-Match $m
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With