Select-string "“ keeping in context
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** *
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.
