2
$res = Invoke-Sqlcmd -Query "select * from customer" -ServerInstance "(localdb)\MSSQLLocalDB" -Database "Database1" -OutputAs DataTables

$res | Where-Object FirstName -eq "John"
$res.Where({$_.FirstName -eq "John"})

This is the output:

Id FirstName City   
-- --------- ----   
 1 John      Augusta
 1 John      Augusta

I was expecting this:

Id FirstName City   
-- --------- ----   
 1 John      Augusta

Id FirstName City   
-- --------- ----   
 1 John      Augusta
Rod
  • 14,529
  • 31
  • 118
  • 230
  • 2
    Pipe to `Format-Table` to force separate tables: `$res | Where-Object FirstName -eq "John" | Format-Table` and `$res.Where({$_.FirstName -eq "John"}) | Format-Table` – zett42 Jun 11 '22 at 14:54
  • 2
    PowerShell optimizes the output for you and concatenates similar objects with the same properties into one table ... neat huh? – Olaf Jun 11 '22 at 14:55
  • @Olaf is that sarcasm, lol – Rod Jun 11 '22 at 14:56
  • 2
    Actually not ... for me that is a convenience function. If you don't want it you can use the tip zett42 gave in his comment – Olaf Jun 11 '22 at 14:58

1 Answers1

5

Building on the helpful comments:

The behavior is by design:

  • Objects output by a given script or a single interactively submitted command line are all sent to the success output stream of a single pipeline.

  • When output from a pipeline is neither captured nor redirected, PowerShell applies for-display output formatting to all output objects in the success output stream of a given pipeline, and if the first output object implicitly selects tabular formatting, all subsequent objects of the same type are formatted as part of the same table that is printed to the host (display, terminal).

    • Things get tricky if subsequent objects are of a different type as well as if they're also [pscustomobject] instances, but with different property sets - see this answer for more information.

If you want to format a given command's output individually, you have three basic options, all of which are suboptimal in case you also want to option to later programmatically process the output, not just format it for display:

  • Send the output directly to the host, using Out-Host:

    $res | Where-Object FirstName -eq "John" | Out-Host     
    
    • This bypasses the success output stream, meaning that this output cannot be captured or redirected.
  • Use a Format-* cmdlet such as Format-Table explicitly:

    $res | Where-Object FirstName -eq "John" | Format-Table
    
    • This sends objects representing formatting instructions rather than the original objects to the success output stream, which the host (as the default output target) renders correctly, but these objects are meaningless for further programmatic processing.
  • Use Out-String (possibly preceded by a call to a Format-* cmdlet to select the kind of formatting and/or control the specifics of the formatting):

    $res | Where-Object FirstName -eq "John" | Out-String
    
    • This sends a single, multi-line string to the success output stream that contains the same representation you would see in the host with Out-Host[1] and since strings always render as-is, you'll see the same host output; in programmatic processing, these strings are relatively more meaningful than the formatting-instruction objects output by Format-* cmdlets, but still amount to a loss of information compared to the original output objects.

[1] Unfortunately, Out-String always appends a trailing newline to this representation; this problematic behavior is the subject of GitHub issue #14444. As zett42 points out, using Out-String -Stream avoids this problem, albeit at the expense of sending the lines of the multi-line string representation individually to the output stream; To avoid that, you can use (... | Out-String -Stream) -join "`n"

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    Thanks, @zett42; I've updated the footnote based on your feedback. As for a future fix: A switch would be an improvement, but, honestly, I wish they'd just fix the default behavior: there was never a good reason for it to begin with. – mklement0 Jun 11 '22 at 20:04