Today’s question involves using the Context parameter:
It’s probably just me, but I’ve never gotten the switch ‘-context 5
or -context 2, 7′ to work predictably – where 5 lines before and after or 2
before and 7 after will come out – have you?
Let’s start by looking at the default behaviour of select-string using the search pattern you’ve seen previously:
PS> Select-String -Path c:\test\*.txt -Pattern “\A\w{5}ABCD”
C:\test\fixedcol.txt:1:12345ABCD123451234512345
C:\test\fixedcol.txt:3:12345ABCD123451234512345
C:\test\fixedcol.txt:4:12345abcd123451234512345
C:\test\fixedcol.txt:6:12345ABCD123451234512345
C:\test\fixedcol2.txt:1:12345ABCD123451234512345
As you can see the line which matches your pattern is returned. Often this is all that is required but there are occasions when you need to be able to put the line into context that is you need to understand how the line containing you pattern relates to the data around it. The is what the context parameter can provide.
If you look at the Select-String help file you will find this information on context.
-Context <Int32[]>
Captures the specified number of lines before and after the line with the match. This allows you to view the match in context.
Required? false
Position? named
Default value
Accept pipeline input? false
Accept wildcard characters? false
The first thing to note is that the parameter takes an array of integers. The first (or only member of the array) tells PowerShell how many lines to show from before and after the matching line while the second member of the array controls the number of lines that are displayed after the matching line. Put simply if you supply one values it controls the number of lines from before and after you match that are displayed but if you specify two values then you explicitly control the lines from before the match with the first value and the lines from after the match with the second. Some examples should make this clear.
I’m going to use a file where we know the contents – it makes the explanations easier. If you run this:
Get-Process | sort CPU -Descending | Out-File -FilePath c:\test\proc.txt –Force
You get a text file with the processes listed by CPU usage. You can examine the file for a particular process – this case let’s look at Word:
PS> Select-String -Path c:\test\*.txt -Pattern “Winword” -SimpleMatch
C:\test\proc.txt:8: 360 25 20368 61728 331 41.89 4976 WINWORD
You know that the file is ordered by CPU usage so what are the processes using similar amounts of CPU to Word?
PS> Select-String -Path c:\test\*.txt -Pattern “Winword” -SimpleMatch -Context 2
C:\test\proc.txt:6: 653 34 66640 94416 286 48.77 3724 powershell
C:\test\proc.txt:7: 1124 29 13912 19336 210 47.71 5868 LiveComm
> C:\test\proc.txt:8: 360 25 20368 61728 331 41.89 4976 WINWORD
C:\test\proc.txt:9: 212 9 2788 10956 78 28.67 4112 SynTPEnh
C:\test\proc.txt:10: 565 35 49172 82572 347 12.50 5660 WWAHost
The matching line is marked with a > symbol. I’ve made it bold in the above listing for emphasis.
If you specify a number such that the file doesn’t have enough lines to display then only those lines that are available will be displayed for instance
Select-String -Path c:\test\*.txt -Pattern “Winword” -SimpleMatch -Context 12
This can only display the seven lines prior to the match so that’s all it does.
What about the situation where you only want the three processes that are using more CPU than Word?
PS> Select-String -Path c:\test\*.txt -Pattern “Winword” -SimpleMatch -Context 3,0
C:\test\proc.txt:5: 1555 74 30660 87176 465 80.70 5308 explorer
C:\test\proc.txt:6: 653 34 66640 94416 286 48.77 3724 powershell
C:\test\proc.txt:7: 1124 29 13912 19336 210 47.71 5868 LiveComm
> C:\test\proc.txt:8: 360 25 20368 61728 331 41.89 4976 WINWORD
This works as well
PS> Select-String -Path c:\test\*.txt -Pattern “Winword” -SimpleMatch -Context 3,$null
C:\test\proc.txt:5: 1555 74 30660 87176 465 80.70 5308 explorer
C:\test\proc.txt:6: 653 34 66640 94416 286 48.77 3724 powershell
C:\test\proc.txt:7: 1124 29 13912 19336 210 47.71 5868 LiveComm
> C:\test\proc.txt:8: 360 25 20368 61728 331 41.89 4976 WINWORD
The one thing you can’t do is this:
PS> Select-String -Path c:\test\*.txt -Pattern “Winword” -SimpleMatch -Context 3,
>>
PowerShell expects something after the comma and will prompt you to supply it.
The converse holds true if you want the lines that occur after the match. You can use a 0 as the first element:
PS> Select-String -Path c:\test\*.txt -Pattern “Winword” -SimpleMatch -Context 0,3
> C:\test\proc.txt:8: 360 25 20368 61728 331 41.89 4976 WINWORD
C:\test\proc.txt:9: 212 9 2788 10956 78 28.67 4112 SynTPEnh
C:\test\proc.txt:10: 565 35 49172 82572 347 12.50 5660 WWAHost
C:\test\proc.txt:11: 276 19 6608 12628 88 9.33 5252 taskhostex
Or $null
PS> Select-String -Path c:\test\*.txt -Pattern “Winword” -SimpleMatch -Context $null,3
> C:\test\proc.txt:8: 360 25 20368 61728 331 41.89 4976 WINWORD
C:\test\proc.txt:9: 212 9 2788 10956 78 28.67 4112 SynTPEnh
C:\test\proc.txt:10: 565 35 49172 82572 347 12.50 5660 WWAHost
C:\test\proc.txt:11: 276 19 6608 12628 88 9.33 5252 taskhostex
You can’t leave the first element blank
PS> Select-String -Path c:\test\*.txt -Pattern “Winword” -SimpleMatch -Context ,3
At line:1 char:76
+ Select-String -Path c:\test\*.txt -Pattern “Winword” -SimpleMatch -Context ,3
+ ~
Missing argument in parameter list.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingArgument
This leads to the situation where you need to display a different number of lines before and after the match:
PS> Select-String -Path c:\test\*.txt -Pattern “Winword” -SimpleMatch -Context 3,2
C:\test\proc.txt:5: 1555 74 30660 87176 465 80.70 5308 explorer
C:\test\proc.txt:6: 653 34 66640 94416 286 48.77 3724 powershell
C:\test\proc.txt:7: 1124 29 13912 19336 210 47.71 5868 LiveComm
> C:\test\proc.txt:8: 360 25 20368 61728 331 41.89 4976 WINWORD
C:\test\proc.txt:9: 212 9 2788 10956 78 28.67 4112 SynTPEnh
C:\test\proc.txt:10: 565 35 49172 82572 347 12.50 5660 WWAHost
What happens if you have multiple matches and their contexts overlap?
PS> Select-String -Path c:\test\*.txt -Pattern “PowerShell” -SimpleMatch
C:\test\proc.txt:9: 669 34 67544 62228 286 54.99 3724 powershell
C:\test\proc.txt:12: 473 23 69112 86616 366 11.73 1688 powershell_ise
C:\test\proc.txt:19: 346 13 39576 44540 207 3.15 280 PowerShell
This file shows matches on lines 9, 12 and 19 so let’s try this
PS> Select-String -Path c:\test\*.txt -Pattern “PowerShell” -SimpleMatch -Context 4,5
C:\test\proc.txt:5: 1840 91 35836 75652 545 142.49 5308 explorer
C:\test\proc.txt:6: 618 23 68524 28788 194 108.84 5072 SkyDrive
C:\test\proc.txt:7: 1377 29 15668 23440 210 95.07 5868 LiveComm
C:\test\proc.txt:8: 211 9 2792 4516 78 59.98 4112 SynTPEnh
> C:\test\proc.txt:9: 669 34 67544 62228 286 54.99 3724 powershell
C:\test\proc.txt:10: 560 35 53480 88760 370 22.95 2656 WWAHost
C:\test\proc.txt:11: 240 8 1952 2340 71 12.32 5580 TabTip
> C:\test\proc.txt:12: 473 23 69112 86616 366 11.73 1688 powershell_ise
C:\test\proc.txt:13: 254 9 4684 9940 86 11.31 6136 RuntimeBroker
C:\test\proc.txt:14: 285 14 4116 4724 84 10.19 5252 taskhostex
C:\test\proc.txt:15: 82 5 2008 6148 55 7.52 2416 conhost
C:\test\proc.txt:16: 305 14 17608 5088 184 5.19 404 IAStorIcon
C:\test\proc.txt:17: 337 8 2180 536 76 4.79 376 InputPersonalization
C:\test\proc.txt:18: 409 12 4416 5464 79 4.26 5276 taskhost
> C:\test\proc.txt:19: 346 13 39576 44540 207 3.15 280 powershell
C:\test\proc.txt:20: 125 5 2820 744 70 2.61 908 splwow64
C:\test\proc.txt:21: 347 13 12180 804 183 2.40 4788 PopUp_DM
C:\test\proc.txt:22: 335 10 2576 1756 83 2.20 4636 AdobeARM
C:\test\proc.txt:23: 249 21 6628 540 129 1.44 5220 SRSPremiumPanel
C:\test\proc.txt:24: 387 11 3436 12280 83 0.94 3828 WSHost
I’ve highlighted the lines that actually match.
Starting with the first match you get 4 lines before it as requested. There should be 5 lines after the match BUT the next match is only 3 lines on and you asked for 5 lines after that. The lines before the last match overlap the lines after the second match. The lines after the last match are shown as requested.
At first glance it looks like the command hasn’t worked but what seems to be happening is that only unique lines are displayed.
I looked at the individual matches
$finds = Select-String -Path c:\test\*.txt -Pattern “PowerShell” -SimpleMatch -Context 4,5
for ($i=0; $i -le $finds.count; $i++){$finds[$i]; “###”*8}
and received this output (I’ve split the display so you can see what is produced.
First match:
C:\test\proc.txt:5: 1840 91 35836 75652 545 142.49 5308 explorer
C:\test\proc.txt:6: 618 23 68524 28788 194 108.84 5072 SkyDrive
C:\test\proc.txt:7: 1377 29 15668 23440 210 95.07 5868 LiveComm
C:\test\proc.txt:8: 211 9 2792 4516 78 59.98 4112 SynTPEnh
> C:\test\proc.txt:9: 669 34 67544 62228 286 54.99 3724 powershell
C:\test\proc.txt:10: 560 35 53480 88760 370 22.95 2656 WWAHost
C:\test\proc.txt:11: 240 8 1952 2340 71 12.32 5580 TabTip
########################
Correct number before but restricted output after
Second match:
> C:\test\proc.txt:12: 473 23 69112 86616 366 11.73 1688 powershell_ise
C:\test\proc.txt:13: 254 9 4684 9940 86 11.31 6136 RuntimeBroker
C:\test\proc.txt:14: 285 14 4116 4724 84 10.19 5252 taskhostex
C:\test\proc.txt:15: 82 5 2008 6148 55 7.52 2416 conhost
C:\test\proc.txt:16: 305 14 17608 5088 184 5.19 404 IAStorIcon
C:\test\proc.txt:17: 337 8 2180 536 76 4.79 376 InputPersonalization
########################
Nothing before and correct output after the match
Last match:
C:\test\proc.txt:18: 409 12 4416 5464 79 4.26 5276 taskhost
> C:\test\proc.txt:19: 346 13 39576 44540 207 3.15 280 powershell
C:\test\proc.txt:20: 125 5 2820 744 70 2.61 908 splwow64
C:\test\proc.txt:21: 347 13 12180 804 183 2.40 4788 PopUp_DM
C:\test\proc.txt:22: 335 10 2576 1756 83 2.20 4636 AdobeARM
C:\test\proc.txt:23: 249 21 6628 540 129 1.44 5220 SRSPremiumPanel
C:\test\proc.txt:24: 387 11 3436 12280 83 0.94 3828 WSHost
########################
One line before the match and correct number after the match.
This confirms that if a line has appeared in a previous match you won’t see it again. Is there a way to see the full context for each match? Unfortunately, Select-String doesn’t appear to provide that capability directly. A little bit of working with the output should enable this.
Select-String -Path c:\test\*.txt -Pattern “PowerShell” -SimpleMatch -Context 4,5 |
foreach {
#matching line
$padlength = (” {0}:{1:00}: ” -f $_.Path, $_.LineNumber).Length
$pad = ” “*$padlength
$_.Context.PreContext | foreach {$_.Trim().Insert(0,$pad)}
“”
” {0}:{1:00}: {2}” -f $_.Path, $_.LineNumber, ($_.Line).Trim()
“”
$_.Context.PostContext | foreach {$_.Trim().Insert(0,$pad)}
“”
“”
}
Run the select-string as before. For each of the matches find the length of the formatted path and line number and create a blank string of that length.
If you examine the MatchInfo type that Select-String produces you will see a Property called Context. If you examine that you will see it contains the collection of data for the pre and post context. (There are also display versions of the context).
For each line in the pre-context insert the pad characters at the beginning. Display the formatted match line and then display the post-context data. I’ve inserted some blank lines to help format the display
You will get output like this:
1840 91 35836 75652 545 142.49 5308 explorer
618 23 68524 28788 194 108.84 5072 SkyDrive
1377 29 15668 23440 210 95.07 5868 LiveComm
211 9 2792 4516 78 59.98 4112 SynTPEnh
C:\test\proc.txt:09: 669 34 67544 62228 286 54.99 3724 powershell
560 35 53480 88760 370 22.95 2656 WWAHost
240 8 1952 2340 71 12.32 5580 TabTip
473 23 69112 86616 366 11.73 1688 powershell_ise
254 9 4684 9940 86 11.31 6136 RuntimeBroker
285 14 4116 4724 84 10.19 5252 taskhostex
211 9 2792 4516 78 59.98 4112 SynTPEnh
669 34 67544 62228 286 54.99 3724 powershell
560 35 53480 88760 370 22.95 2656 WWAHost
240 8 1952 2340 71 12.32 5580 TabTip
C:\test\proc.txt:12: 473 23 69112 86616 366 11.73 1688 powershell_ise
254 9 4684 9940 86 11.31 6136 RuntimeBroker
285 14 4116 4724 84 10.19 5252 taskhostex
82 5 2008 6148 55 7.52 2416 conhost
305 14 17608 5088 184 5.19 404 IAStorIcon
337 8 2180 536 76 4.79 376 InputPersonalization
82 5 2008 6148 55 7.52 2416 conhost
305 14 17608 5088 184 5.19 404 IAStorIcon
337 8 2180 536 76 4.79 376 InputPersonalization
409 12 4416 5464 79 4.26 5276 taskhost
C:\test\proc.txt:19: 346 13 39576 44540 207 3.15 280 powershell
125 5 2820 744 70 2.61 908 splwow64
347 13 12180 804 183 2.40 4788 PopUp_DM
335 10 2576 1756 83 2.20 4636 AdobeARM
249 21 6628 540 129 1.44 5220 SRSPremiumPanel
387 11 3436 12280 83 0.94 3828 WSHost
Not the greatest of displays but you do get to see the data. It should be possible to do this through PowerShell’s formatting system but that’s a post for another day.
Bottom line – the context parameter only displays unique lines so you won’t necessarily get what you expect if there are multiple matches in a file.