Last January I left the world of scrappy Internet companies to join the Windows Automation team at a Fortune 500 firm. The group was just starting to warm up to PowerShell and one of my first tasks was to give a presentation on what “Good PowerShell” looks like. I took a few scripts we’d been working on and polished them up. Verb-Noun. Get-Help. -WhatIf. Parameterization. Discovering things from the environment instead of asking for them. Embracing the Pipeline. Error handling and using Exceptions. That sorta stuff.
And then I presented. This is how you do things, and this is why you do them. These are choices that I made, and this is why I chose them.
It went over well enough. But. My scripts put a fair amount of output on the console via Write-Host, and someone challenged that. In support of their argument they sent me several links, one of which was Write-Host Considered Harmful. By Jeffrey Snover. The guy responsible for PowerShell.
*Gulp*
The meat of Jeffrey’s argument isn’t wrong. Write-Host is problematic in certain circumstances. This is entirely because Write-Host bypasses redirection within PowerShell. There is no way to capture Write-Host output within a single PowerShell session.
The upside is that Write-Host also bypasses the pipeline. So if a script is emitting objects to the pipeline, Write-Host will not stick a bunch of nasty text in the middle of your PSObject nirvana. This code:
Write-Host "Hello World." > .\out.txt
Doesn’t put anything in .\out.txt.
"Hello World." | Write-Host | ForEach-Object { Write-Host "Goodbye World." }
Hello World.
The above code never says Goodbye because Write-Host put nothing on the pipeline. This pattern is absolutely a misuse of Write-Host, and if you do that you will quickly discover your mistake and correct it.
Now, what happens when a function is called that uses Write-Host and returns objects?
function n {
Write-Host "Helpful Message."
return "Useful Object."
}
n | ForEach-Object { Write-Host ("Pipeline Received: " + $PSItem) }
Helpful Message.
Pipeline Received: Useful Object.
As we’ve already covered, Write-Host output doesn’t pollute the pipeline with text, so the pipeline has my Useful Object while the console got a Helpful Message. What’s wrong with that? Jeffrey says everything. I say it’s fine for nearly all use cases.
Neither of us are wrong.
To really understand Jeffrey’s position you need to read his original Monad Manifesto. In the traditional command-line world, when you write something like a | b | c, all of those subsequent steps are reading one program’s text output to find the information you need and do something useful with it. Sed, grep, regular expressions. Your local *nix geek loves these tools and does wonderful things with them, but in Jeffrey’s words acting on unstructured text is “clumsy, lossy, imprecise.” It would be much better if a could output its objects directly to b and c. That’s what PowerShell (Monad) delivered.
Jeffrey wants your scripts to emit objects and not fall back into the world of consuming unstructured text. A noble cause.
But Write-Host doesn’t produce text that other PowerShell scripts can consume.
And if your PowerShell scripts are being called by something that flattens Write-Host‘s output into STDOUT along with everything else, guess what? You’ve left the realm of objects and are back to unstructured text, Write-Host or not.
Now, I do believe that Write-Host should only be used in one place: the body of a script that is meant to be invoked by itself or as the final stage of a pipeline.
I don’t use Write-Host in modules, scripts meant to be dot-sourced, or functions within a stand-alone a script. My reasoning for this is that functions are intended to be called by other PowerShell scripts or commands and writing to the console should be purely the domain of the caller. My functions return objects, Throw errors, and use Write-Verbose, Write-Warning, Write-Debug, and Write-Progress for other information that might be useful.
When I’m writing a script that isn’t going to emit objects, because it’s being invoked by an automation system that can’t use them or by a person to perform a complex process, Write-Host is a perfectly reasonable thing to use, and I’m not going to apologize for it. Not even to Jeffrey “I invented PowerShell” Snover.