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-AllMatches
switch.- Input is implicitly line by line only in the following scenarios:
- Letting
Select-String
itself read and process files with the-Path
or-LiteralPath
parameter. - Using
Get-Content
output as input, which streams line by line by default (though usingSelect-String
's-Path
/-LiteralPath
or pipingGet-ChildItem
output 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
-AllMatches
present and more than one object stored in the.Matches
collection, the member-access enumeration operation$_.Matches.Groups[1].Value
is insufficient for obtaining each match's.Groups[1].Value
property - you need to iterate over the$_.Matches
collection and apply.Groups[1].Value
to 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
.