The good
Whenever you want output on your screen in different colours, Write-Host is your tool.
There’s even a TechNet article on it: ‘Display Output in Color Using Windows PowerShell‘.
The bad
“Every time you use Write-Host, you kill a puppy…”
That’s one of my favorite quotes from the (in)famous PowerShell MVP Don Jones.
The bad very well explained by Jeffrey Snover (PowerShell daddy) in his blog post ‘Write-Host Considered Harmful‘.
In a nutshell: It interferes with automation.
The whole point of PowerShell is automation!
The ugly
People starting to listen. They’re not using Write-Host anymore.
But they still want to use colours, so for blue Write-Verbose is what they use.
For yellow, they use Write-Warning.
In my opinion, this is also terribly wrong!
Why? Write-Verbose doesn’t show itself by default. You’ll have to use the -Verbose parameter in order to get it to show output.
Write-Error provides an output like an error, in red. Not just the text you provide as input, but a bunch of stuff you’ll probably don’t want if your goal is to have red output.
1 2 3 4 |
PS E:\> write-error 'hello' write-error 'hello' : hello + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException |
So that leaves us Write-Warning. This gives a nice yellow output with no fluffy stuff like Write-Error does… just the input you’ve provided in the colour yellow.
But Write-Host offers more colours than just these three. So I can understand, from a colour perspective, why people started to abuse the Write-* cmdlets.
A practical look.
Whenever I script, I want to pipe output to another command which in turn uses it as input.
Using Write-Host will make that impossible…
Try it with Write-Output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
PS JeffWouters.nl> Write-Output "hello" | gm TypeName: System.String Name MemberType Definition ---- ---------- ---------- Clone Method System.Object Clone() CompareTo Method int CompareTo(System.Object value), int CompareTo(string strB) Contains Method bool Contains(string value) CopyTo Method System.Void CopyTo(int sourceIndex, char[] destination, int destinationIndex,... EndsWith Method bool EndsWith(string value), bool EndsWith(string value, System.StringCompari... Equals Method bool Equals(System.Object obj), bool Equals(string value), bool Equals(string... GetEnumerator Method System.CharEnumerator GetEnumerator() GetHashCode Method int GetHashCode() GetType Method type GetType() GetTypeCode Method System.TypeCode GetTypeCode() IndexOf Method int IndexOf(char value), int IndexOf(char value, int startIndex), int IndexOf... IndexOfAny Method int IndexOfAny(char[] anyOf), int IndexOfAny(char[] anyOf, int startIndex), i... Insert Method string Insert(int startIndex, string value) IsNormalized Method bool IsNormalized(), bool IsNormalized(System.Text.NormalizationForm normaliz... LastIndexOf Method int LastIndexOf(char value), int LastIndexOf(char value, int startIndex), int... LastIndexOfAny Method int LastIndexOfAny(char[] anyOf), int LastIndexOfAny(char[] anyOf, int startI... Normalize Method string Normalize(), string Normalize(System.Text.NormalizationForm normalizat... PadLeft Method string PadLeft(int totalWidth), string PadLeft(int totalWidth, char paddingChar) PadRight Method string PadRight(int totalWidth), string PadRight(int totalWidth, char padding... Remove Method string Remove(int startIndex, int count), string Remove(int startIndex) Replace Method string Replace(char oldChar, char newChar), string Replace(string oldValue, s... Split Method string[] Split(Params char[] separator), string[] Split(char[] separator, int... StartsWith Method bool StartsWith(string value), bool StartsWith(string value, System.StringCom... Substring Method string Substring(int startIndex), string Substring(int startIndex, int length) ToCharArray Method char[] ToCharArray(), char[] ToCharArray(int startIndex, int length) ToLower Method string ToLower(), string ToLower(System.Globalization.CultureInfo culture) ToLowerInvariant Method string ToLowerInvariant() ToString Method string ToString(), string ToString(System.IFormatProvider provider) ToUpper Method string ToUpper(), string ToUpper(System.Globalization.CultureInfo culture) ToUpperInvariant Method string ToUpperInvariant() Trim Method string Trim(Params char[] trimChars), string Trim() TrimEnd Method string TrimEnd(Params char[] trimChars) TrimStart Method string TrimStart(Params char[] trimChars) Chars ParameterizedProperty char Chars(int index) {get;} Length Property System.Int32 Length {get;} |
And now the same with Write-Host:
1 2 3 4 5 6 7 |
PS JeffWouters.nl> Write-Host "hello" | gm hello Get-Member : No object has been specified to the get-member cmdlet. At line:1 char:24 + Write-Host "hello" | gm <<<< + CategoryInfo : CloseError: (:) [Get-Member], InvalidOperationException + FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand |
A few months ago I had the pleasure to script against a product called AppAssure from Dell.
Well, they use Write-Host in their cmdlets.
All the output provided by those cmdlets was useless to me.
I wasn’t able to use their output and therefor had to do me some fancy scripting to check if my action had any result.
It would have been much, much easier if I could just have checked the output fmo the command and be done with it.
But apparenly they thought that would have been to easy, or something…
The alternative
The goal would be to output text in a colour. That’s it, pure and simple.
In my opinion, Microsoft doesn’t provide a viable alternative to Write-Host in that perspective.
So, here’s where the PowerShell community steps in ๐ Jeffery Hicks, PowerShell MVP, has written a function called ‘Out-ConsoleGraph‘.
This allows you to use the colours as you did with Write-Host, and much, much more.
Just take a look, I’m pretty sure you’re going to like it ๐
The solution
I would love for Microsoft to include something native in PowerShell which allows colourized output.
Maybe a parameter attached to Write-Output, or a new cmdlet all together in order not to possibly break any existing scripts out there.
For now, just remember: Don’t use Write-Host unless you don’t care about the output and automation of the task.
I personally believe that Write-Host is very useful. Not for returning objects, like you are stating, but for tracing and logging! In the PowerShell frameworks I write, I like to have loads of tracing/logging information on what the scripts and commands are doing. Everything is logged automatically to a transcript file; if an (unattended) script failed somehow, the transcript file will help me to trace the issue.
I’m using my own replacement Write-Host function that identifies the server where the Write-Host was executed in a remoted/workflow scenario by automatically prefixing text with “[servername]” and logging the text to a server specific log file. (the framework uses a controller script to coordinate installations and configurations on several servers at once, so it’s very useful to see what text came from what server). My Write-Host function ultimately calls the the PowerShell native version.
The best practice is to use Write-Verbose for outputting tracing text to the host, but i started to dislike the “Verbose:” prefix (and the explicit use of -verbose switch). If the requirement would be not to use Write-Host anymore, I would introduce my own Write-Verbose (framework specific “Write-FrmwrkVerbose”) to use instead of Write-Host for verbose output combined with a $FrmwrkVerbosePreference global variable
I’d love to show you what we’ve built.
@victorvogelpoel
Hi Victor,
You’ve basically touched possible single exception to the rule ๐
I’ve done something similar, using write-host to output results (not objects) to the screen.
If an error occurs, the line is shown in the colour ‘red’ and therefor the helpdesk can immediatly see something is going wrong.
… and that’s where the alternative by Jeffery Hicks comes into play… no need for Write-Host ๐
As for your example, why not use PowerShell remoting and and do Write-output (“[“+($Env:ComputerName)+”][Just some message]”).
I totally feel your comment about write-verbose though. That’s why I stated there isn’t a viable alternative, in my opinion, built into the shell.
Jeff.
When Microsoft gives us a flag to:
* Remove the VERBOSE: prefix from Write-Verbose.
* Remove the nonsensical additional information returned in Write-Error
Then they can pry Write-Host from my cold, dead fingers.
Hi, Cody,
Take a look at the VerbosePreference variable.
Regarding Write-Error, it outputs .net error objects and should be used as such.
I think the question becomes what you use Write-Error for.
It’s my personal believe that a script should always be able to run non-interactively and therefor any user-interaction becomes mute.
Write-Host writes to the host, to the user.
However, if you want/need colorful output to the screen, I agree with you that write-host has a valid use-case.
The problem I have with it is that you can’t do anything with it, like redirect the output to a file.
The right command for the right use-case. If Write-Host fits your needs, personally have no issue with you using it ๐
Jeff.
โOut-ConsoleGraphโ. link is dead