Time certainly has a way of getting away from you. I didn’t realize that it had been about two months since my last post. Things have been busy at work, recently, and I just haven’t had the time to post anything.
Over the past couple of weeks, I have been participating in the 2011 Scripting Games. That was certainly an interesting experience. The judging is not quite over with yet, but no matter what the scores are, it was a blast to dig in and find out some new things.
Normally, I like to post about a specific type of script, but today, I felt like it would be time better spent on one of the things I learned during these Scripting Games. To some of the veterans, this is going to be a serious review, but I would like to throw my hat in the ring to document what I learned.
As anyone who has worked with Powershell knows (hopefully), Powershell is an object-based language. No matter what you do in Powershell, you are dealing with objects. Let me say that again: NO MATTER WHAT YOU DO IN POWERSHELL, YOU ARE DEALING WITH OBJECTS! Ok, now that we have that out of the way, why make such a big deal out of it? I’m glad you asked.
Usually, when you are writing a script (or using the shell interactively) you want to have some type of output. From my very early days in programming, it was usually to calculate something and put it on the screen. It was also put out to the screen as text. In Powershell, you have the ability to write any text you want out to the console…but should you? This is where most people who start in Powershell have to think a little differently.
There are many ways to push information out of a script where you can use it, but today I want to focus on two cmdlets: Write-Host and Write-Output. Now, you might be asking yourself, “Self…Why would I need one over the other?” Let’s find out. First, in your Powershell console, enter the following:
and you will see the following description:
The Write-Host cmdlet customizes output. You can specify the color of text by using the ForegroundColor parameter, and you can specify the background color by using the BackgroundColor parameter. The Separator parameter lets you specify a string to use to separate displayed objects. The particular result depends on the program that is hosting Windows PowerShell.
Write-Host only works with formatting text. Once you do this, there are no longer any objects to work with, so you really have nothing to work with after a Write-Host operation. To see this in action, enter the following commands on your console:
Get-Process | Write-Host
Get-Process | Write-Host | Get-Member
The first command will give you some rather strange information. It will list all your process names, but it will list the .ToString() rendering of each object that comes out. Since each object is a type of System.Diagnostics.Process object, you get that information and the name of the process in parentheses. Further, in the second command, you can see that no objects actually make it to the Get-Member cmdlet. As a result, you get the friendly message that states:
Get-Member : No object has been specified to the get-member cmdlet. At line:1 char:30 + get-process | write-host | get-member <<<< + CategoryInfo : CloseError: (:) [Get-Member], InvalidOperationException + FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Commands.GetMemberCommand
Now, don’t get me wrong, there is a time and place to use Write-Host. If you want to let the user know that something is going on, Write-Host away! Tell them that you are grabbing some information, or even sometimes that the normal path to get something done isn’t working and the script is making a detour (you did try to handle unexpected circumstances, right?).
So, how to get information out of Powershell otherwise? Well, since Powershell likes to deal in objects, we should give it what it wants. Most any cmdlet you look at will accept an object as input from the pipeline. Even if you write scripts, you should be working towards allowing input of objects from the pipeline so that your scripts work just like the pre-loaded cmdlets. This is where Write-Output comes in. Let’s take a look at the description for this cmdlet:
The Write-Output cmdlet sends the specified object down the pipeline to the next command. If the command is the last command in the pipeline, the object is displayed in the console.
Write-Output sends objects down the primary pipeline, also known as the “output stream” or the “success pipeline.” To send error objects down the error pipeline, use Write-Error.
This cmdlet is typically used in scripts to display strings and other objects on the console. However, because the default behavior is to display the objects at the end of a pipeline, it is generally not necessary to use the cmdlet. For example, “get-process | write-output” is equivalent to “get-process”.
From your scripts, this is a much better option to get the data out so that it can be consumed by the next cmdlet in the pipeline (assuming your script needs to send something out). Let’s try our example again, only this time with the Write-Output cmdlet instead:
Get-Process | Write-Output
Get-Process | Write-Output | Get-Member
Now, I admit, this example is a little simplistic. Even the description above indicates that using Get-Process | Write-Output is redundant, but it goes to show my point. This time, when you pipe to the Get-Member after Write-Output, you can see that it does return the object information it received from the System.Diagnostic.Process object.
I had not recognized this difference until I participated in the Scripting Games and I still consider myself a decent (not expert) scripter. I can usually get my work done with Powershell, although sometimes I still get a headache from it. Even so, I can’t imagine doing my work without Powershell. The time I put in on the front end saves my team and me countless hours of work.
Leave me a comment and let me know if this article was helpful to you.