PowerShell: write-output only writes one object

powershell

Solution

Just to clarify: the problem is only a display problem:

- When outputting to the console, if the first object is table-formatted (if `Format-Table` is applied, which happens implicitly in your case), the display columns are locked in based on that first object's properties. Since your second output object shares no properties with the first one, it contributes nothing to the table display and is therefore effectively invisible.

- By contrast, if you programmatically process the script's output - assign it to a variable or send its output through the pipeline to another command - both objects will be there.

- See Charlie Joynt's answer for a helpful example of assigning the two output objects to separate variables.

The simplest solution to the display problem is to explicitly format for display each input object individually - see below.

For a given single object inside a script, you can force formatted to-display (to-host) output with `Out-Host`:

$object | Out-Host # same as: Write-Output $object | Out-Host

Note, however, that this outputs directly and invariably to the console only and the object is then not part of the script's data output (the objects written to the success output stream, the stream with index `1`). In other words: if you try to assign the script's output to a variable or send its output to another command in a pipeline, that object won't be there.

See below for why `Out-Host` is preferable to `Write-Host`, and why it's better to avoid `Write-Host` in most situations.

To apply the technique ad hoc to a given script's output as a whole, so as to make sure you see all output objects, use:

./some-script.ps1 | % { $_ | Out-String }  # % is the built-in alias of ForEach-Object

Note that here too you could use `Out-Host`, but the advantage of using `Out-String` is that it still allows you to capture the for-display representation in a file, if desired.

Here's a simple helper function (filter) that you can put in your `$PROFILE`:

# Once defined, you can use: ./some-script.ps1 | Format-Each
Filter Format-Each { $_ | Out-String }

PetSerAl's suggestion - `./some-script.ps1 | Format-List` - works in principle too, but it switches the output from the usual table-style output to list-style output, with each property listed on its own line, which may be undesired. Conversely, however, `Format-Each`, if an output object is (implicitly) table-formatted, prints a header for each object.

Why `Write-Output` doesn't help:

`Write-Output` doesn't help, because it writes to where output objects go by default anyway: the aforementioned success output stream, where data should go.

If the output stream's objets aren't redirected or captured in some form, they are sent to the host by default (typically, the console), where the automatic formatting is applied.

Also, use of `Write-Output` is rarely necessary, because simply not capturing or redirecting a command or expression implicitly writes to the success stream; another way of putting it: `Write-Output` is implied.

Therefore, the following two statements are equivalent:

Write-Output $object  # write $object to the success output stream
$object               # same; *implicitly* writes $object to the success output stream

Why use of `Write-Host` is ill-advised, both here and often in general:

Assuming you do know the implications of using `Write-Host` in general - see below - you could use it for the problem at hand, but `Write-Host` applies simple `.ToString()` formatting to its input, which does not give you the nice, multi-line formatting that PowerShell applies by default. Thus, `Out-Host` (and `Out-String`) were used above, because they do apply the same, friendly formatting.

Contrast the following two statements, which print a hash-table (`[hashtable]`) literal:

# (Optional) use of Write-Output: The friendly, multi-line default formatting is used.
# ... | Out-Host and ... | Out-String would print the same.
PS> Write-Output @{ foo = 1; bar = 'baz' }

Name                           Value
----                           -----
bar                            baz
foo                            1

# Write-Host: The hashtable's *entries* are *individually* stringified
#             and the result prints straight to the console.
PS> Write-Host @{ foo = 1; bar = 'baz' }

System.Collections.DictionaryEntry System.Collections.DictionaryEntry    

`Write-Host` did two things here, which resulted in near-useless output:

The `[hashtable]` instance's entries were enumerated and each entry was individually stringified.

The `.ToString()` stringification of hash-table entries (key-value pairs) is `System.Collections.DictionaryEntry`, i.e., simply the type name of the instance.

The primary reasons for avoiding `Write-Host` in general are:

It outputs directly to the host (console) rather than to PowerShell's success output stream.

- As a beginner, you may mistakenly think that `Write-Host` is for writing results (data), but it isn't.

In bypassing PowerShell's system of streams, `Write-Host` output cannot be redirected - that is, it can neither be suppressed nor captured (in a file or variable).

- That said, starting with PowerShell v5.0, you can now redirect its output via the newly introduced information stream (number `6`; e.g., `./some-script.ps1 6>write-host-output.txt`); however, that stream is more properly used with the new `Write-Information` cmdlet. By contrast, `Out-Host` output still cannot be redirected.

That leaves just the following legitimate uses of `Write-Host`:

Creating end-user prompts and colored for-display-only representations:

Your script may have interactive prompts that solicit information from the user; using `Write-Host` - optionally with coloring via the `-ForegroundColor` and `-BackgroundColor` parameters - is appropriate, given that prompt strings should not become part of the script's output and users also provide their input via the host (typically via `Read-Host`).

Similarly, you can use `Write-Host` with selective coloring to explicitly create friendlier for-display-only representations.

Quick prototyping: If you want a quick-and-dirty way to write status/diagnostic information directly to the console without interfering with a script's data output.

- However, it is generally better to use `Write-Verbose` and `Write-Debug` in such cases.

Problem

I'm learning PowerShell and a vast number of articles I read strongly discourages the use of write-host telling me it's "bad practice" and almost always, the output can be displayed in another way. So, I'm taking the advice and try to avoid use of write-host. One suggestion I found was to use write-output instead. As far as I understand, this puts everything in a pipeline, and the output is executed at the end of the script (?). However, I have problems outputting what I want. This example demonstrates the issue: ``` $properties = @{'OSBuild'="910ef01.2.8779"; 'OSVersion'="CustomOS 3"; 'BIOSSerial'="A5FT-XT2H-5A4B-X9NM"} $object = New-Object –TypeName PSObject –Prop $properties Write-output $object $properties = @{'Site'="SQL site"; 'Server'="SQL Server"; 'Database'="SQL Database"} $object = New-Object –TypeName PSObject –Prop $properties Write-Output $object ``` This way I get a nice output of the first object displaying the OS data, but the second object containing the SQL data is never displayed. I've tried renaming the variable names, and a bunch of other different stuff, but no luck. While troubleshooting this problem, I found similar problems with suggestions to just replace write-output with write-host. This gets me very confused. Why are some people strongly discouraging write-host, while other people encourage it? And how exactly do I output these two objects in a fashionably manner? I do not fully understand the pipeline mechanism of write-output.

Original source