# Update 2019-12-28 Powershell 7 Select-String default highlighting

Update 2019-12-28: It’s very exciting to see that since Powershell 7, the Select-String has highlighting (internal name: emphasis) by default. It uses similar way (index, length) to find and highlight the matches. The emphasis uses negative colors based on your PowerShell background and text colors. To disable the emphasis, use the -NoEmphasis switch. So I highly recommend everyone to switch to Powershell 7 (RC is supported by Microsoft), it has also many other new powerful features.

BTW, in Powershell 7, Select-String -AllMatches is set as $false by default. I think it would be nice to have an inverse switch -NoAllMatches just like -NoEmphasis, and let -AllMatches to be$true by default.

Update 2019-12-31: I just found a workaround here, by specifying $PSDefaultParameterValues['Select-String:AllMatches'] =$true in the Profile.ps1. I don’t know if you have the same feeling as the mine, this feature is killing, it will help me for many other things :)

Powershell 7 Select-String default highlighting demo:

The original post before the Emphasis has been introduced in Powershell 7:

Select-String in Powershell is a very powerful cmdlet to search a string or a pattern in input or files. It’s very much like the famous command-line grep in Unix. But from my personal point of view, it’s a little bit pity that Select-String doesn’t highlight the matching patterns, so I will show you in this post how to make it possible (more or less) with Select-ColorString.

# Trace-Word

First of all, I must mention another powershell cmdlet Trace-Word that I read on Prateek Singh’s blog ridicurious.com.

Let me show you a screenshot of his Trace-Word to let you have an idea about what it can do:

Indeed, I was deeply impressed when I read his post, the color in Powershell string search results had been one of my most expected Powershell functionalities. Prateek Singh made it, thanks!

When I checked the code source of Trace-Word, I found the cmdlet logic is:

1. Firstly reads the input content line by line:

 $content | ForEach-Object {...}  2. And then splits each line by white-space:  $_.split() | Where-Object {
-not [string]::IsNullOrWhiteSpace($_) } | ForEach-Object{...}  3. At last checks each splitted token against the searching words:  if($Token -like "*$Word*") {$before, $after =$Token -Split "$Word"; ... }  4. Now we have $before, $Word,$after, so just need to Write-Host $Word with color to highlight the wanted$Word.

That’s done, pretty cool and quite straightforward, nothing complicated, I like it so much.

I contacted Prateek to ask if I can use his idea to write something similar but with another method, he said YES and that comes my Select-ColorString, thanks Prateek again.

# Select-ColorString

Although Prateek Singh’s Trace-Word is wonderful enough, I still want a bit more capabilities: the regex and the customizable color choice.

The first thing that I thought about the regex is Select-String which I’m using almost everyday with sls.

Sometimes I was obliged to use the DOS command-line findstr due to that Select-String catches the input too earlier before it is been displayed a pure string on console screen. But findstr just finds what you want among what is shown on the screen. Although $input | Out-String | Select-String might solve the issue sometimes but it’s not sexy to use 2 cmdlets to do one single task and sometimes this workaround even doesn’t work. Powershell Select-String returns some MatchInfo objects, from its MemberType, the Matches property is what I will use to color the matching patterns. The Index key gives the index of the first char of the matching pattern in a given line string, with that I know from where I could Write-Host with color. PS> 'a is good, b is good too' | sls good -AllMatches | gm TypeName:Microsoft.PowerShell.Commands.MatchInfo Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() RelativePath Method string RelativePath(string directory) ToString Method string ToString(), string ToString(string directory) Context Property Microsoft.PowerShell.Commands.MatchInfoContext Context {get;set;} Filename Property string Filename {get;} IgnoreCase Property bool IgnoreCase {get;set;} Line Property string Line {get;set;} LineNumber Property int LineNumber {get;set;} Matches Property System.Text.RegularExpressions.Match[] Matches {get;set;} Path Property string Path {get;set;} Pattern Property string Pattern {get;set;} PS> 'a is good, b is good too' | sls good -AllMatches | % matches Groups : {0} Success : True Name : 0 Captures : {0} Index : 5 Length : 4 Value : good Groups : {0} Success : True Name : 0 Captures : {0} Index : 16 Length : 4 Value : good  So for my Select-ColorString, its logic is: 1. Split the input content in lines.  foreach ($line in $Content) {...}  2. Find all the matches in a given line. $paramSelectString = @{
Pattern       = $Pattern AllMatches =$true
CaseSensitive = $CaseSensitive }$matchList = $line | Select-String @paramSelectString  3. Write without color for the string before the match. $index = 0
foreach ($myMatch in$matchList.Matches) {
$length =$myMatch.Index - $index Write-Host$line.Substring($index,$length) -NoNewline
...
}

4. Right after, write the match with color.

 foreach ($myMatch in$matchList.Matches) {
...
$paramWriteHost = @{ Object =$line.Substring($myMatch.Index,$myMatch.Length)
NoNewline       = $true ForegroundColor =$ForegroundColor
BackgroundColor = $BackgroundColor } Write-Host @paramWriteHost ... }  5. Recalculate the index for the next match in the same line. $index = 0
foreach ($myMatch in$matchList.Matches) {
...
$index =$myMatch.Index + $myMatch.Length }  6. When there’s no more matches in the same line, just write without color all the rest. $index = 0
foreach ($myMatch in$matchList.Matches) {
...
$index =$myMatch.Index + $myMatch.Length } Write-Host$line.Substring(\$index)


That’s all, let’s see a demo on Select-ColorString.

# Select-ColorString demo

The demo reads in real-time a test file and use Select-ColorString to highlight the keyword warn

# Select-String & -Split

In fact Powershell -split operator can also take regex pattern, and is as powerful as Select-String can do in terms of searching pattern. The reason that I chose Select-String instead of -split is because :

1. Select-String makes sense to ‘port’ Unix grep on Powershell, they’re both for searching patterns and display them.

2. -split just splits the line by pattern, you still need to iterate on each splitted token and perform a -like or -match operation, which might take more time to display then Select-String does, as the later stores the matches already, it just needs to move the index and display the matches in color. But to be honest, I’ve never tested the execution duration difference between -split and Select-String, maybe -split is faster.

When I have time, I will write new function based on -split with regex to test its power.

# Trace-Word & Select-ColorString

Both of them are in my toolkit, and I use them in different scenarios.

• When I only need to search patterns based on words, I will use Trace-Word, as it can display different colors on different words. A typical use case is monitoring the log files which have some keywords like info, warning, error, etc. The output is much more beautiful.
• When I need to search patterns which include white space for example, I will use Select-ColorString as it takes regex and it doesn’t split the line by white space in advance

BTW, I also set an alias on each of them:

PS> Set-Alias tw Trace-Word
PS> Set-Alias scs Select-ColorString
`

# Update 2018-11-19 on new switch -MultiColorsForSimplePattern

I added a new switch -MultiColorsForSimplePattern last week. This switch enables the Select-ColorString to display the different keywords in different colors just like Trace-Word. This is very useful at least for me to search some keywords like error, warning in the log files.

There’s a limitation on this new switch that the multicolors only works for simple pattern which contains only keywords separated by “|” as shown in above screenshot. And it cannot be used with regex, this is because by using regex, the color selection will take much more time than the simple keywords. Maybe in the future I will add a new switch -MultiColorsForRegexPatternWithFastCpu.

# Select-ColorString source code

Finally, you can find the the source code of Select-ColorString on Github.

As I forced to use only a few of the original Select-String’s parameters, Select-ColorString cannot do everything that Select-String does, that’s why I said more or less at the beginning of this post.

Some better ways that I think to archive the goal is whether use ValueFromRemainingArguments to send all the remaing non-handled Select-ColorString parameters to Select-String, whether let Microsoft Powershell team to modify directly the Types.ps1xml

Tags:

Updated: