Hey, Scripting Guy! Question

Hey, Scripting Guy! I am not into all those fancy scripts you seem to write. I am just a basic, everyday network administrator, and I simply need to use scripts to make my job easier. Because of this, I am not interested in displaying progress bars, writing stuff to Excel or Word or even PowerPoint. I am interested simply in having my Windows PowerShell script display information on the screen. What is the best way to do this?

- CO

SpacerHey, Scripting Guy! Answer

Hi CO,

At times it seems like only yesterday, but when I started out in the field of IT, I also was a basic, everyday network administrator. The days were never boring because the problems were never the same. I would show up in my office, and there would be a message from a user stating they could not get logged on to the network; while I was resetting their password, I would check my e-mail and find out that someone else was having problems creating a macro in Office Excel. As I checked my project board, I saw that I needed to get a new network drop installed in a remote office. I also had to teach a class in the afternoon on basic Office Outlook for a dozen new users. Yep, being a basic, everyday network administrator was a lot of fun. I never got bored.

The one thing I never had was free time. Back then I used to drink coffee, but I don't think I ever actually finished a cup of coffee before it was cold. I actually got to where I could drink cold coffee without cringing and shuddering. Because I was so busy running from problem to problem, I seldom wrote scripts. When I did write a script, it would display information on the screen because I always ran the scripts in an interactive fashion. Years later, after I began working at Microsoft, my good friend Jason and I went to Hong Kong where I learned a new appreciation for interactive displays. Here is a good picture of Victoria Harbor I took from a hydrofoil on my way to Macau.

Image of Victoria Harbor

 

CO, when displaying information on the screen, the most important thing to remember is that it is easy. In many cases, you do not have to do anything more complicated than to run the cmdlet. When you do, you are automatically rewarded with nicely formatted output that is displayed on the screen:

Image of formatted output displayed on screen


The reason the output is nicely formatted is the Windows PowerShell team created several format.ps1xml files that are used to control the way different objects are formatted when they are displayed. These XML files are located in the Windows PowerShell installation directory. Luckily, there is an automatic variable, $pshome, that can be used to refer to the Windows PowerShell installation directory. To obtain a listing of all the format.ps1xml files that are installed on your computer, use the Get-ChildItem cmdlet and specify a path that will retrieve any file with the name format in it. Pipeline the resulting fileinfo objects to the Select-Object cmdlet and choose the name property. This is seen here:

PS C:\> Get-ChildItem -Path $PSHOME/*format* | Select-Object -Property name

Name
----
certificate.format.ps1xml
dotnettypes.format.ps1xml
filesystem.format.ps1xml
help.format.ps1xml
powershellcore.format.ps1xml
powershelltrace.format.ps1xml
registry.format.ps1xml

These format.ps1xml files are used by the Windows PowerShell Extended Type System to determine how to display objects. This is required because most objects do not know how to display themselves. Because the format files are XML files, it is possible to edit them to change the default display behavior. This should not be undertaken lightly, though, because the files are rather complicated. If you wish to edit the files, make sure you have a good backup copy of the files before you start messing around with them. Direct manipulation of the format.ps1xml files could result in unexpected behavior. It is also possible to write your own format.ps1xml file (in fact Marc Van Orsouw [MOW] created his own format data file to tune the default output format for the Trace-Route function he wrote as a guest commentator for the 2009 Summer Scripting Games.

The dotnettypes.format.ps1xml file is used to control the output that is displayed by a number of the cmdlets (Get-Process, Get-Service, Get-EventLog, etc.) that return .NET Framework objects. A portion of the dotnettypes.format.ps1xml file is seen in the next image. This is the section of the file that controls the output from the Get-Process cmdlet that is seen in the previous image. Under the <TableHeaders> section, each column heading is specified by the <TableColumnHeader> tag. And under the <TableColumnHeader>, there are three nodes: <Label>, <Width>, and <Alignment>. <Label> controls the column heading, <Width> controls how wide the column is, and <Alignment> is used to specify the alignment (left, center, right) of the data within the column. This is seen here:

Image of code specifying table alignment


To display information in the Command Prompt window, you do not need to worry about format XML files or anything like that. You can rely upon the defaults and allow Windows PowerShell to make the decision for you. To display a string, you place the string in quotation marks and it is displayed in the Command Prompt window. This is seen here:

PS C:\> "this string is displayed to the console"
this string is displayed to the console
PS C:\>

The important thing to keep in mind is that when the string is shown in the Command Prompt window, it retains its type; that is, it is still a string. This is seen here:

PS C:\> "this string is displayed to the console" | Get-Member


   TypeName: System.String

Name             MemberType            Definition
----             ----------            ----------
Clone            Method                System.Object Clone()
CompareTo        Method                int CompareTo(System.Object value), i...
Contains         Method                bool Contains(string value)
CopyTo           Method                System.Void CopyTo(int sourceIndex, c...
EndsWith         Method                bool EndsWith(string value), bool End...
Equals           Method                bool Equals(System.Object obj), bool ...
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(...
IndexOfAny       Method                int IndexOfAny(char[] anyOf), int Ind...
Insert           Method                string Insert(int startIndex, string ...
IsNormalized     Method                bool IsNormalized(), bool IsNormalize...
LastIndexOf      Method                int LastIndexOf(char value), int Last...
LastIndexOfAny   Method                int LastIndexOfAny(char[] anyOf), int...
Normalize        Method                string Normalize(), string Normalize(...
PadLeft          Method                string PadLeft(int totalWidth), strin...
PadRight         Method                string PadRight(int totalWidth), stri...
Remove           Method                string Remove(int startIndex, int cou...
Replace          Method                string Replace(char oldChar, char new...
Split            Method                string[] Split(Params char[] separato...
StartsWith       Method                bool StartsWith(string value), bool S...
Substring        Method                string Substring(int startIndex), str...
ToCharArray      Method                char[] ToCharArray(), char[] ToCharAr...
ToLower          Method                string ToLower(), string ToLower(Syst...
ToLowerInvariant Method                string ToLowerInvariant()
ToString         Method                string ToString(), string ToString(Sy...
ToUpper          Method                string ToUpper(), string ToUpper(Syst...
ToUpperInvariant Method                string ToUpperInvariant()
Trim             Method                string Trim(Params char[] trimChars),...
TrimEnd          Method                string TrimEnd(Params char[] trimChars)
TrimStart        Method                string TrimStart(Params char[] trimCh...
Chars            ParameterizedProperty char Chars(int index) {get;}
Length           Property              System.Int32 Length {get;}

If you use one of the Out-* cmdlets such as Out-Host or Out-Default, you destroy the object-oriented nature of the string. That is, it is no longer an instance of a system.string .NET Framework class. This is seen here:

PS C:\> "this string is displayed to the console" | Out-Host | Get-Member
this string is displayed to the console
Get-Member : No object has been specified to the get-member cmdlet.
At line:1 char:66
+ "this string is displayed to the console" | Out-Host | Get-Member <<<<
    + CategoryInfo          : CloseError: (:) [Get-Member], InvalidOperationEx
   ception
    + FullyQualifiedErrorId : NoObjectInGetMember,Microsoft.PowerShell.Command
   s.GetMemberCommand

PS C:\>

As a best practice you should avoid using the Out-Host cmdlet or the Out-Default cmdlet unless there is a reason for using it. This is because you lose your object after you send the output to these two cmdlets. The only reason for using Out-Host is to use the –paging parameter. This is seen here:

PS C:\> Get-WmiObject -Class win32_process | Out-Host -Paging

__GENUS                    : 2
__CLASS                    : Win32_Process
__SUPERCLASS               : CIM_Process
__DYNASTY                  : CIM_ManagedSystemElement
__RELPATH                  : Win32_Process.Handle="0"
__PROPERTY_COUNT           : 45
__DERIVATION               : {CIM_Process, CIM_LogicalElement, CIM_ManagedSyste
                             mElement}
__SERVER                   : VISTA
__NAMESPACE                : root\cimv2
__PATH                     : \\VISTA\root\cimv2:Win32_Process.Handle="0"
Caption                    : System Idle Process
CommandLine                :
CreationClassName          : Win32_Process
CreationDate               :
CSCreationClassName        : Win32_ComputerSystem
CSName                     : VISTA
Description                : System Idle Process
ExecutablePath             :
ExecutionState             :
Handle                     : 0
HandleCount                : 0
InstallDate                :
KernelModeTime             : 151488730096
MaximumWorkingSetSize      :
MinimumWorkingSetSize      :
Name                       : System Idle Process
OSCreationClassName        : Win32_OperatingSystem
OSName                     : Microsoftr Windows VistaT Business |C:\Windows|\De
                             vice\Harddisk0\Partition1
OtherOperationCount        : 0
OtherTransferCount         : 0
PageFaults                 : 0
PageFileUsage              : 0
ParentProcessId            : 0
PeakPageFileUsage          : 0
PeakVirtualSize            : 0
PeakWorkingSetSize         : 0
Priority                   : 0
PrivatePageCount           : 0
<SPACE> next page; <CR> next line; Q quit

If you are not using the paging parameter, there is no advantage to using the Out-Host cmdlet. From a display perspective, the following commands are identical:

Get-Process

Get-Process | Out-Host

Get-Process | Out-Default

In fact, on most systems Out-Default and Out-Host do the same thing. This is because by default Out-Host is the default outputter. The only reason to use Out-Default would be if you anticipated changing the default outputter, and you did not want to rewrite the script. By using Out-Default the output from the script will always go to the default outputter, which may or may not be the host.

CO, that is about all there is to say for now about writing to the host. Join us tomorrow as we will continue talking about handling output from scripts. If you want to keep up to date with the Scripting Guys, you can follow us on Twitter. You can also look us up on Facebook. Until tomorrow peace!

Ed Wilson and Craig Liebendorfer, Scripting Guys