In sed one can output all matches or e.g. only the first:
$ echo -e 'a1\nb2\nb3'
a1
b2
b3
# print all matches
$ echo -e 'a1\nb2\nb3' | sed -n '/b\(.\)/s//\1/p'
2
3
# print only first match
$ echo -e 'a1\nb2\nb3' | sed -n '0,/b\(.\)/s//\1/p'
2
How can I do this with Powershell?
I get only the first match:
@'
>> a1
>> b2
>> b3
>> '@ | Select-String 'b(.*)' | ForEach-Object{$_.Matches.Groups[1].Value}
2
CodePudding user response:
Your PowerShell here-string (
@'<newline>...<newline>'@) is a single object (string) that happens to be a multi-line string.To
Select-String, each input object is a "line", and by default it looks only for the first match on each "line" - unless you specify the-AllMatchesswitch.- Input is implicitly line by line only in the following scenarios:
- Letting
Select-Stringitself read and process files with the-Pathor-LiteralPathparameter. - Using
Get-Contentoutput as input, which streams line by line by default (though usingSelect-String's-Path/-LiteralPathor pipingGet-ChildItemoutput is much more efficient). - Piping output from calls to external programs, whose stdout output is also streamed line by line.
- Letting
- Input is implicitly line by line only in the following scenarios:
With
-AllMatchespresent and more than one object stored in the.Matchescollection, the member-access enumeration operation$_.Matches.Groups[1].Valueis insufficient for obtaining each match's.Groups[1].Valueproperty - you need to iterate over the$_.Matchescollection and apply.Groups[1].Valueto each element.
Therefore, to get all matches:
@'
a1
b2
b3
'@ |
Select-String 'b(.*)' -AllMatches |
ForEach-Object{ $_.Matches.ForEach({ $_.Groups[1].Value }) }
Output:
2
3
To get only the first match, your command works as-is, for the reasons explained above.
Alternatively, you could have split your here-string into individual lines first, in which case the rest of your command would have worked as-is to get all matches:
@'
a1
b2
b3
'@ -split '\r?\n' | # Split the multi-line string into indiv. lines
Select-String 'b(.*)' |
ForEach-Object{ $_.Matches.Groups[1].Value }
To get only the first match, with line-by-line output, pipe the above to Select-Object -First 1.
Note: Select-String has a -List switch that looks for at most one match, but it only applies to files as input, either via its own -Path / -LiteralPath parameter, or by piping file-info objects from Get-ChildItem.
