I'm aware that Get-Content has a -Tail parameter, but as far as I can tell, there's simply no way to get it to accept piped input.
So how do I get something like this to work? I just want to be able to select first X or last X lines from any preceding command, without the hassle of creating temp files simply so I can grab the first/last lines from it.
# This won't work as Get-Content demands a -Path to a file
Get-Item .\* | Get-Content -Tail 10
Some added finagling will likely be required depending on whether the first command is providing objects or strings as output; if you can include such intermediary steps in your reply, that'd be awesome. I expect something like Out-String -stream might be the done thing?
I've scoured the internet and came up empty-handed. Thanks in advance for any help.
PowerShell's pipeline is based on objects rather than lines of text, and Mathias R. Jessen's helpful answer has the object-oriented angle covered:
Select-Object
-First
/ -Last
selects only the first / last N objects - whatever their type - and then renders them to the display using PowerShell's rich for-display output-formatting system.
Given that the formatting of a single object can span multiple lines (consider the display output when you submit $PSVersionTable
), there's no guarantee that the output will be limited to N lines.
If, by contrast, your intent is to limit the output to a given number of display lines - irrespective of how many objects the result corresponds to:
Insert an Out-String
call with the -Stream
switch parameter before the Select-Object
call. PowerShell v5+ even has a built-in wrapper function for this, oss
.
This performs the for-display formatting to a string, with -Stream
then emitting the lines of the result one by one.
A simple example that illustrates the two approaches and how they differ:
# Create 10 sample objects that render with Format-List
# (each property on its own line)
$objects =
1..10 | ForEach-Object { [pscustomobject] @{ a=1*$_; b=2*$_; c=3*$_; d=4*$_; e=5*$_ } }
Write-Verbose -Verbose 'First 2 *objects*:'
# Select the first 2 *objects*, which are then rendered,
# each with multiple lines.
$objects | Select-Object -First 2
Write-Verbose -Verbose 'First 2 *lines*:'
# Create the for-display representation as a string up front,
# then select the first 2 *lines*
$objects | oss | Select-Object -First 2
This outputs the following:
VERBOSE: First 2 *objects*:
a : 1
b : 2
c : 3
d : 4
e : 5
a : 2
b : 4
c : 6
d : 8
e : 10
VERBOSE: First 2 *lines*:
a : 1
Note:
PowerShell's output formatting often involves a leading and trailing empty line, which is why the output from the oss
-based command starts with an empty line and only shows one property value (you could address that by eliminating blank lines as follows:
$objects | oss | Where-Object { $_.Trim() } | Select-Object -First 2
)
An important use case for oss
is quick-and-dirty text searches in object output via Select-String
:
E.g., the following would show only the lines that contain the digit (string) 3
:
$objects | oss | Select-String '3'
Where-Object
, is the right approach.Sadly, Select-String
doesn't have the oss
stringification logic built in, even though it arguably should - see GitHub issue #10726.