Printer Friendly Version      Send     
Click to Rate and Give Feedback
Related Articles
Don Jones demonstrates a Windows PowerShell-based inventory tool and guides you through the process that goes into building such a solution.

By Don Jones (November 2008)
This month, The Scripting Guys take a close look at the WMI infrastructure. Along the way, they provide some helpful scripts that can serve as a starting point for learning more and accomplishing useful administrative tasks.

By The Microsoft Scripting Guys (November 2008)
Ever wish Windows PowerShell would launch with a work environment tailored to your needs? Don Jones demonstrates how you can use profiles to customize the Windows PowerShell shell.

By Don Jones (October 2008)
The Scripting Guys discuss Socrates and revisit the topic of querying an XML file . This time, however, the XML file is structured so that rather than using child nodes, additional property values are configured as attributes.

By The Microsoft Scripting Guys (October 2008)
More ...
Articles by this Author
Don Jones demonstrates a Windows PowerShell-based inventory tool and guides you through the process that goes into building such a solution.

By Don Jones (November 2008)
Ever wish Windows PowerShell would launch with a work environment tailored to your needs? Don Jones demonstrates how you can use profiles to customize the Windows PowerShell shell.

By Don Jones (October 2008)
Despite its object oriented nature, Windows PowerShell is also able to parse complicated strings. Don Jones demonstrates how you can do this in Windows PowerShell using Select-String.

By Don Jones (September 2008)
Windows PowerShell 2.0 is in the works and the latest CTP provides some great new capabilities. Here’s a look at remoting—one of the most exciting new features you'll find in Windows PowerShell 2.0 CTP2.

By Don Jones (August 2008)
Don Jones discusses filtering functions, the pipeline, custom objects, and how ping-pong balls hold the secret to understanding Windows PowerShell.

By Don Jones (July 2008)
Version 1 of Windows PowerShell offers less than impressive Active Directory management capabilities. Find out how you can use add-ins to get more powerful ways to work with Active Directory objects from within Windows PowerShell.

By Don Jones (May 2008)
An overview of signing your scripts for improved security.

By Don Jones (April 2008)
For your users with attention span issues—and even those without—providing status feedback in your scripts is a good idea. Here's all you need to know to provide status in Windows PowerShell.

By Don Jones (March 2008)
More ...
Popular Articles
Microsoft Office Communication Server brings important changes to enterprise telephony. Examine how voice calls are made layer by layer, learn how calls can be routed to various endpoints, and explore the importance of conversations in OCS communications.

By Rajesh Ramanathan (July 2008)
Moving from a legacy voicemail system to a unified messaging platform can be done fairly easily and with little disruption to your end users—as long as you have a solid plan in place. Here’s what you need to know to plan your migration, test your new systems, and implement unified messaging in your organization.

By Jeff Goodwin (July 2008)
When you want to reduce the total cost of ownership of the desktop machines in your organization, application lockdown can be a great help, letting you limit IT issues related to unsupported applications. See how you can use software restriction policies and Group Policy to control the applications being run throughout your IT infrastructure.

By Chris Corio and Durga Prasad Sayana (June 2008)
Virtualization isn’t limited to virtual machines and the virtualization of operating systems. Terminal Services has been abstracting the presentation layer of remotely run applications and desktops for years. Take a close look at key improvements to Terminal Services in Windows Server 2008 and get some tips for using these new features.

By Joshua Schnoll (November 2008)
More ...
Read the Blog
With SP1 installed, ConfigMgr works with Windows Server 2008 just as it does with other versions of Windows. The November 2008 issue of TechNet Magazine offers a quick overview ...
Read more!
The much-anticipated release of Windows Server 2008 introduced significant changes to the OS, adding powerful functionality such as server core, server roles,  read-only DCs, Hyper-V, Terminal Services Gateway, and enhancement support for Internet Protocol version 6 (IPv6). While these changes and new features are beneficial, they ...
Read more!
Virtualization is hot nowadays, but Terminal Services has been abstracting the presentation layer of remotely run applications and desktops for years. A lot has changed over the years, and with Windows Server 2008, Terminal Services has truly become a mature, robust presentation virtualization ...
Read more!
If you’re an OpsMgr 2007 administrator, chances are good that you’ll be creating custom monitoring objects (rules, groups, and so forth), and for each one you build, you have to decide what target to use. That’s a critical decision, but knowing how to go about choosing the correct target is not always clear. Steve ...
Read more!
The November 2008 issue of TechNet Magazine is now available online.   FEATURE ARTICLES                                                                   ...
Read more!
It's been about 8 years since Scott Culp published "The 10 Immutable Laws of Security." It is one of the best and most important essays on computer security ...
Read more!
More ...
Windows PowerShell Looking Good
Don Jones


It's not uncommon to want your scripts to produce nice-looking output, and I'm often asked about the best way to produce things like tables. There are really a couple of ways to go about doing this. The first step is to write a script that puts the data—that is, whatever data it is that I want to display in a formatted fashion—into variables named
$data1, $data2, and $data3. Now I can get down to formatting the data.

The Text Way
The most common way folks tend to approach this task is the same way they'd handle it if they were working in a language such as VBScript, Perl, KiXtart, or another text-oriented language. I might start by writing out a set of column headers to the screen:
Write-Host "ColumnA'tColumnB'tColumnC"
Note that the `t is a special escape sequence in Windows PowerShellTM that inserts a tab character. To write out each line of my table—remembering that my data is in $data1, $data2, and $data3—I have a few choices. One is to simply write the variables, relying on the shell's ability to replace variables with their contents inside double quotation marks:
Write-Host "$data1't$data2't$data3"
Another slightly more attractive way to achieve this uses the –f operator. This technique separates the formatting from the data and makes what I think is an easier-to-read and more easily maintained line of code:
Write-Host "{0}'t{1}'t{3}" –f $data1,$data2,$data3
This entire approach has significant problems, though. First, by relying on the fixed tab stops in the console window, I am setting myself up for some odd formatting. For example, if a particular table row has a 20-character value in the first column, I throw the entire formatting for that row out of whack. Also, since I'm outputting this text by using Write-Host, I'm pretty much limited to a console display.
There's no easy way to send that output to a file or to put it in other formats, should I ever want to do so. Most importantly, this text-based approach completely ignores the inherently object-based shell that I'm working in, failing to take advantage of all the incredible techniques and capabilities that Windows PowerShell offers.
Video
Watch Don Jones demonstrate how you can easily add formatting to your Windows PowerShell output.


The Windows PowerShell Way
Windows PowerShell is an object-oriented shell. That means, ideally, everything you work with should be in objects, allowing the shell to turn things into text displays when needed. But how do you create objects for arbitrary pieces of data?
Continuing my example, I start by creating a blank custom object and storing it in a variable:
 $obj = New-Object PSObject
This gives me a fresh, empty object to work with. I then add my data to the object in the form of properties. To do this, I simply pipe my object to the Add-Member cmdlet. I add something called a NoteProperty, give the property a name (a good idea is to use the column headers as property names), and then insert the data as the values for the properties:
 $obj | Add-Member NotePropertyColumnA $data1
$obj | Add-Member NotePropertyColumnB $data2
$obj | Add-Member NotePropertyColumnC $data3
Now I simply output this object—not to the console, but to the pipeline:
Write-Output $obj
I can then repeat these steps for each table row that I need to output. The following is a short function that accepts a string and outputs an uppercase and lowercase version, along with the original string:
functionStringVersions {
param([string]$inputString)
  $obj = New-Object PSObject
  $obj | Add-Member NoteProperty Original($inputString)
  $obj | Add-Member NoteProperty Uppercase($inputString.ToUpper())
  $obj | Add-Member NoteProperty Lowercase($inputString.ToLower())
  Write-Output $obj
}  
$strings = @("one","two","three")
foreach ($item in $strings) {
StringVersions $item
}
I start with an array of string variables, going through each of them one at a time and sending them to the function. And the function's output is written to the pipeline.
You should note that there are shorter ways to write this code, but I chose this technique because it clearly illustrates the point I want to make. The result, as shown in Figure 1, is a perfectly formatted table. That's because the shell already knows how to format objects in a table.
Figure 1 Windows PowerShell output displayed in a table (Click the image for a larger view)
Whenever an object has four or fewer properties, Windows PowerShell chooses a table automatically. So with no work whatsoever on my part, I now have a great-looking table.

But Wait, There's More!
Cmdlet of the Month: Get-Command
I'm sure you've used Get-Command, or its handy alias (gcm), once or twice to review the list of available Windows PowerShell cmdlets. But you may not know how flexible gcm really is. For instance, if you want to see everything Windows PowerShell can do with a service, run gcm -noun service. Or, if you want to see all of the Windows PowerShell export options, try gcm -verb export. If you just need to see what cmdlets were added by a particular snap-in, such as the PowerShell Community Extensions, try gcm -pssnapin pscx. (You can replace "pscx" with any snap-in name to see that snap-in's cmdlets.)
As you can see, Get-Command is a key player in the discoverability of Windows PowerShell. It allows you to learn what functionality is available without even having to pick up a manual. And note that gcm is a more accurate way of discovering functionality than using a command such as Help *. The Help function only lists available help topics. Any cmdlets that didn't ship with help don't show up in the help listing—even though the cmdlets are there if you need them.

The benefit of this technique goes far beyond tables. When you're working with objects, Windows PowerShell knows how to do a huge range of things. Want your data in a CSV file? Use Export-CSV. Prefer an HTML table? Pipe your objects to ConvertTo-HTML. Need a list format? Pipe them to Format-List. By using objects, you can make use of all the things the shell already knows how to do. Here's a revised example:
functionStringVersions {
  PROCESS {
   $obj = New-Object PSObject
   $obj | Add-Member NoteProperty Original($_)
   $obj | Add-Member NoteProperty Uppercase($_.ToUpper())
   $obj | Add-Member NoteProperty Lowercase($_.ToLower())
   Write-Output $obj
}
}
This time around, I've modified the function to accept pipeline input—this is always a better idea when you're working with objects—and to output to the pipeline.
This function now has its code inside of a PROCESS scriptblock. This means that function will accept pipeline input and will execute the PROCESS scriptblock once for each object that is piped in.
Within the PROCESS scriptblock, the special $_ variable refers to the current pipeline object that's being processed. The practical result of this is that now I can simply pipe in an array of strings:
@("one","two","three") | StringVersions
I get a table because the function is putting its output into the pipeline. At the end of the pipeline, the shell knows to call on its formatting subsystem, which makes the decision to use a table because the objects in the pipeline have fewer than five properties (for more properties, the shell will use a list by default).
But I don't have to rely on the default behavior. I can simply pipe those objects to another cmdlet to have something else done with them:
@("one","two","three") | StringVersions | Format-List
@("one","two","three") | StringVersions | ConvertTo-HTML | Out-File "strings.html"
@("one","two","three") | StringVersions | Export-CSV "strings.csv"
@("one","two","three") | StringVersions | Select Uppercase,Lowercase -unique
Figure 2 shows the resulting HTML from the second example displayed in a Web browser. By simply representing my output data in objects, rather than representing it as simple text, I suddenly now have complete access to a wealth of functionality that is built into the shell—a variety of formatting layouts; HTML conversion; export options; the ability to sort, filter, and group; and more.
Figure 2 Windows PowerShell data output in HTML format (Click the image for a larger view)
This is very powerful stuff. In fact, I will go so far as to suggest that every script you write should produce an object as its output so you can use that output in as many different ways as possible—all without writing a single additional line of code.

Don Jones is an expert in Windows administrative automation and has authored such books as Windows PowerShell: TFM and VBScript, WMI, and ADSI Unleashed. You can reach him through the forums on ScriptingAnswers.com.
© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.
Page view tracker