Select-String
operates on each (stringified on demand[1]) input object.
A multi-line string such as "abc`r`ndef"
is a single input object.
- By contrast,
"abc", "def"
is a string array with two elements, passed as two input objects.
To ensure that the lines of a multi-line string are passed individually, split the string into an array of lines using PowerShell's -split
operator: "abc`r`ndef" -split "`r?`n"
- (The
?
makes the `r
optional so as to also correctly deal with `n
-only (LF-only, Unix-style) line endings.)
In short:
"abc`r`ndef" -split "`r?`n" | Select-String -Pattern "abc"
The equivalent, using a PowerShell string literal with regular-expression (regex) escape sequences (the RHS of -split
is a regex):
"abc`r`ndef" -split '\r?\n' | Select-String -Pattern "abc"
It is somewhat unfortunate that the Select-String
documentation talks about operating on lines of text, given that the real units of operations are input objects - which may themselves comprise multiple lines, as we've seen.
Presumably, this comes from the typical use case of providing input objects via the Get-Content
cmdlet, which outputs a text file's lines one by one.
Note that Select-String
doesn't return the matching strings directly, but wraps them in [Microsoft.PowerShell.Commands.MatchInfo]
objects containing helpful metadata about the match.
Even there the line metaphor is present, however, as it is the .Line
property that contains the matching string.
[1] Optional reading: How Select-String
stringifies input objects
If an input object isn't a string already, it is converted to one, though possibly not in the way you might expect:
Loosely speaking, the .ToString()
method is called on each non-string input object[2]
, which for non-strings is not the same as the representation you get with PowerShell's default output formatting (the latter is what you see when you print an object to the console or use Out-File
, for instance); by contrast, it is the same representation you get with string interpolation in a double-quoted string (when you embed a variable reference or command in "..."
, e.g., "$HOME"
or "$(Get-Date)"
).
Often, .ToString()
just yields the name of the object's type, without containing any instance-specific information; e.g., $PSVersionTable
stringifies to System.Management.Automation.PSVersionHashTable
.
# Matches NOTHING, because Select-String sees
# 'System.Management.Automation.PSVersionHashTable' as its input.
$PSVersionTable | Select-String PSVersion
In case you do want to search the default output format line by line, use the following idiom:
... | Out-String -Stream | Select-String ...
However, note that for non-string input it is more robust and preferable for subsequent processing to filter the input by querying properties with a Where-Object
condition.
That said, there is a strong case to be made for Select-String
needing to implicitly apply Out-String -Stream
stringification, as discussed in this GitHub feature request.
[2] More accurately, .psobject.ToString()
is called, either as-is, or - if the object's ToString
method supports an IFormatProvider
-typed argument - as .psobject.ToString([cultureinfo]::InvariantCulture)
so as to obtain a culture-invariant representation - see this answer for more information.