Mixing up output and display is the top reason PowerShell functions return garbage or print nothing.
PowerShell separates output (objects sent down the pipeline, which callers can capture) from display (what the host shows you). Anything not captured is output; Write-Output is the explicit form but rarely needed.
For messages, use the cmdlet for the purpose, because each writes to its own stream, not to the output:
Write-Verbose(shown with-Verbose),Write-Debug,Write-Warning,Write-Error,Write-InformationandWrite-Hostfor user-facing text;Write-Hostcan add colour.
That way $result = My-Function captures only data, not chatter.
Format-Table, Format-List and Format-Wide turn objects into formatting instructions for display. Use them only at the end of a pipeline: after Format-Table you no longer have the original objects, so sorting, exporting or reading properties breaks. To get text, use Out-String; to write to a file, Out-File or Set-Content; to save data, prefer Export-Csv or ConvertTo-Json.
The -f format operator builds strings: '{0} is {1:N1} GB' -f $name, $size, with .NET format codes like N2, P0 (percent) and yyyy-MM-dd for dates. Out-Null or $null = ... discard output you do not want.
Example
function Get-Info {
[CmdletBinding()] param()
Write-Verbose 'collecting' # only shown with -Verbose
Write-Host 'progress...' -ForegroundColor Cyan # display, not output
[pscustomobject]@{ Name = 'disk'; UsedGB = 41.53 }
}
$r = Get-Info # screen shows: progress...
$r.UsedGB # 41.53 (the object was captured)
Get-Info -Verbose | Out-Null # VERBOSE: collecting
Get-ChildItem $PSHOME -File | Select-Object -First 3 Name, Length |
Format-Table -AutoSize # display only, at the very end
'{0} is {1:N1} GB' -f 'disk', 41.53 # disk is 41.5 GB
$text = Get-Process -Id $PID | Out-String # render as one string
$text.GetType().Name # StringRemember
- Output goes down the pipeline; Write-Host only displays
- Write-Verbose / Warning / Error keep messages out of captured data
- Format-* only at the end; Out-String, Export-Csv or ConvertTo-Json for data
Watch out for
Get-Process | Format-Table | Export-Csv: the CSV contains formatting objects, not processes.