Select-string "“ keeping in context

Richard Siddaway
10 min read
Share:

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.