In this article, I am trying to work on a task, where I was asked to print the line number and the line, and highlight the line number and the matched text, If the line matches a pattern.
To get better understanding on How search happens in powershell, you can read my article Select-String.
So, let use start writing commands.
First, we need to get the matching lines.
Which can be done by commadn given below.
In this command, I am working on a log file, where I am trying to find a match for an Ip Address.
$MatchInfos = Select-String -path .\TestFiles\Linux_2k.log -Pattern '(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})';
Select-String returns an array of match infos instances, and the matches are stored in $MatchInfos variable, for futher processing.
$MatchInfos is an array, so, we can use foreach to loop over it.
In each iteration, $MatchInfo object will be set an instance of MatchInfo class.
And, the MatchInfo Class contains all the pattern matching details of the current matched line.
The Loop skeleton looks like this.
foreach($MatchInfo in $MatchInfos) {
}
Next, we are going to extract and print the line number.
Write-Host -Object "$($MatchInfo.LineNumber) :: " -ForegroundColor 'Yellow' -BackgroundColor 'White' -NoNewLine;
$MatchInfo.LineNumber contains the sequence number of current line in the entire file.
-ForegroundColor sets the foreground color of printed text.
-BackgroundColor sets the background color of printed text.
Then, we initialize some variables.
$Groups = $MatchInfo.Matches.Groups;
$GroupSlot = $Groups[1];
$FoundAt = $GroupSlot.Index;
$Length = $GroupSlot.Length;
$Line = $MatchInfo.Line;
$FoundAt contains the index at which the Pattern was found in the Line $Line.
$Length contains the length of the matching substring.
$Line contains the text of current line.
In a general scenario, our text can be divided into three parts,
- first part is before the pattern match.
- Second part is the matching string itself.
- Third is the text after the match.
The first part of the string, before match is printed by below command.
Write-Host -Object $Line.Substring(0, $FoundAt) -NoNewLine;
We have passed -NoNewLine argumetn to Write-Host, so that new line break will not be added.
After that we print the matching substring, and then the remaining string.
But, we have to take care of an edge case, where the matching string could extend till the end of the Line, in those cases we won' t have any third part.
So, we have to make sure to add line break after printing second part.
if( $FoundAt + $Length -ge $Line.Length) {
Write-Host -Object $Line.Substring($FoundAt, $Length) -ForegroundColor 'Red' -BackgroundColor 'Blue';
} else {
Write-Host -Object $Line.Substring($FoundAt, $Length) -ForegroundColor 'Red' -BackgroundColor 'Blue' -NoNewLine;
Write-Host -Object $Line.Substring($FoundAt + $Length) ;
}
$FoundAt + $Length -ge $Line.Length checks if current match extends till the end of the line.
When we run the code, we get result shown below in the screenshot.
In the above, screenshot you can see the hightlighted line number on the left, and matching pattern between start and end of the line.
When we put entire code together, it looks like snippet below.
$MatchInfos = Select-String -path .\TestFiles\Linux_2k.log -Pattern '(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})';
foreach($MatchInfo in $MatchInfos) {
Write-Host -Object "$($MatchInfo.LineNumber) :: " -ForegroundColor 'Yellow' -BackgroundColor 'White' -NoNewLine;
$Groups = $MatchInfo.Matches.Groups;
$GroupSlot = $Groups[1];
$FoundAt = $GroupSlot.Index;
$Length = $GroupSlot.Length;
$Line = $MatchInfo.Line;
Write-Host -Object $Line.Substring(0, $FoundAt) -NoNewLine;
if( $FoundAt + $Length -ge $Line.Length) {
Write-Host -Object $Line.Substring($FoundAt, $Length) -ForegroundColor 'Red' -BackgroundColor 'Blue';
} else {
Write-Host -Object $Line.Substring($FoundAt, $Length) -ForegroundColor 'Red' -BackgroundColor 'Blue' -NoNewLine;
Write-Host -Object $Line.Substring($FoundAt + $Length) ;
}
}
As as a next step, you can put this code in a function, which takes two parameters
1. File Path and,
2. Pattern String.
After that, this solution should work for all of those files and patterns.