66

I am trying to find a pattern in files. When I get a match using Select-String I do not want the entire line, I just want the part that matched.

Is there a parameter I can use to do this?

For example:

If I did

select-string .-.-.

and the file contained a line with:

abc 1-2-3 abc

I'd like to get a result of just 1-2-3 instead of the entire line getting returned.

I would like to know the Powershell equivalent of a grep -o

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Skyler
  • 1,159
  • 2
  • 13
  • 22
  • If part of your regexp is used to filter the lines you print, but you don't want to print that part, you can use lookahead and lookbehind groups (same as `grep -ohP`). Suppose you want to get the number in `keep 123 good` but not in `drop 456 nogood` then you can use `(Select-String '(?>=keep )123(?= good)' myfile.txt).Matches.Value`, result: `123` – Andrew Spencer Dec 06 '21 at 11:11

8 Answers8

43

Or just:

Select-String .-.-. .\test.txt -All | Select Matches
Keith Hill
  • 194,368
  • 42
  • 353
  • 369
  • 10
    To add some additional information: `Select[-Object] Matches` outputs _custom objects_ with a `.Matches` property copied from `Select-String`'s `[Microsoft.PowerShell.Commands.MatchInfo]`-typed output objects. If you want to output the actual _text_ captured by the regex, use `ForEach-Object { $_.Matches[0].Value }` instead, assuming there's only 1 match per line. If there are multiple - which there could be due to `-All[Matches]`, the `grep -o` equivalent - you can use `ForEach-Object { $_.Matches.Value }` in PSv3+; in PSv2 you'd have to enumerate the `$_.Matches` collection explicitly. – mklement0 May 26 '18 at 07:22
  • 11
    `echo "abcd" | Select-String -Pattern '(ab)' | Select Matches` returns {0} – john k Oct 12 '21 at 19:56
  • Yeah this just returns stuff like `{0, 0, 0, 0, ...}` and not the actual matched values.... – Douglas Gaskell Nov 11 '22 at 18:47
  • 1
    and `ForEach-Object { $_.Matches.Value }` returns one blank line per match... – Douglas Gaskell Nov 11 '22 at 18:48
36

David's on the right path. [regex] is a type accelerator for System.Text.RegularExpressions.Regex

[regex]$regex = '.-.-.'
$regex.Matches('abc 1-2-3 abc') | foreach-object {$_.Value}
$regex.Matches('abc 1-2-3 abc 4-5-6') | foreach-object {$_.Value}

You could wrap that in a function if that is too verbose.

Steven Murawski
  • 10,959
  • 41
  • 53
31

I tried other approach: Select-String returns property Matches that can be used. To get all the matches, you have to specify -AllMatches. Otherwise it returns only the first one.

My test file content:

test test1 alk atest2 asdflkj alj test3 test
test test3 test4
test2

The script:

select-string -Path c:\temp\select-string1.txt -Pattern 'test\d' -AllMatches | % { $_.Matches } | % { $_.Value }

returns

test1 #from line 1
test2 #from line 1
test3 #from line 1
test3 #from line 2
test4 #from line 2
test2 #from line 3

Select-String at technet.microsoft.com

stej
  • 28,745
  • 11
  • 71
  • 104
  • 1
    I couldn't get this one working, i only have access to PS 1.0 and it doesn't look like -AllMatches is recognized in 1.0 at least. Thanks anyways! – Skyler Apr 30 '09 at 15:28
  • Hmm, I work with v2, ctp3. I have no possibility to try to solve that on v1, so sorry.. – stej Apr 30 '09 at 20:41
  • 1
    Support for matches was added in v2, in addition to context. – JasonMArcher May 01 '09 at 02:05
16

In the spirit of teach a man to fish ...

What you want to do is pipe the output of your select-string command into Get-member, so you can see what properties the objects have. Once you do that, you'll see "Matches" and you can select just that by piping your output to | **Select-Object** Matches.

My suggestion is to use something like: select linenumber, filename, matches

For example: on stej's sample:

sls .\test.txt -patt 'test\d' -All |select lineNumber,fileName,matches |ft -auto

LineNumber Filename Matches
---------- -------- -------
         1 test.txt {test1, test2, test3}
         2 test.txt {test3, test4}
         3 test.txt {test2}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jaykul
  • 15,370
  • 8
  • 61
  • 70
13

None of the above answers worked for me. The below did.

Get-Content -Path $pathToFile | Select-String -Pattern "'test\d'" | foreach {$_.Matches.Value}

Get-Content -Path $pathToFile | # Get-Content will divide into single lines for us

Select-String -Pattern "'test\d'" | # Define the Regex

foreach {$_.Matches.Value} # only return the value of the Object's Matches field. (This allows for multiple result matches.)

not2qubit
  • 14,531
  • 8
  • 95
  • 135
7r4c0r
  • 321
  • 3
  • 5
  • 1
    powerShell 7.1 requires `-AllMatches` to be used with `select-string` to return all matches, like `"test1 test2 test3" | Select-String -Pattern "test\d" -AllMatches | foreach {$_.Matches.Value}`. Only first match is returned by `select-string` otherwise – oleksa Dec 04 '20 at 10:38
  • Working fine without `-All` on PWSH 7.2.2, @oleksa. – André Levy Apr 08 '22 at 00:02
11

Instead of piping to % or select you can use simpler .prop Member Enumeration syntax, which magically works on multiple elements:

(Select-String .-.-. .\test.txt -All).Matches.Value

or less parentheses:

$m = Select-String .-.-. .\test.txt -All
$m.Matches.Value
Carl Walsh
  • 6,100
  • 2
  • 46
  • 50
5

If you don't want to use ForEach operator, you can only use pipes and Select -Expand

For example, to get only the path after C:\, you could use :

Get-ChildItem | Select-String -Pattern "(C:\\)(.*)" | Select -Expand Matches | Select -Expand Groups | Where Name -eq 2 | Select -Expand Value

Where Name -eq 2 only selects the second match of the regex pattern specified.

Adrien Constant
  • 301
  • 3
  • 3
  • for picking out the matched string when also using lookahead/lookbehind this is the approach that worked for me. `cat myfile | select-string "(?<=before).+(?=after)" | Select -Expand Matches | Select -ExpandProperty Value` – Chris F Carroll Sep 22 '21 at 15:18
2

You can use the System.Text.RegularExpressions namespace:

http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regex.aspx

David McEwing
  • 3,320
  • 18
  • 16