IN THIS CHAPTER
Although previous chapters in this book have emphasized PowerShell’s ease of use for scripters familiar with other scripting languages such as Bash and VBScript, the PowerShell language introduces a number of key new concepts that enable scripters to take their automation tasks to a new level of capability, organization, and clarity. The goal of this chapter is to explore these new concepts, provide specific examples to demonstrate each concept, and to illustrate the underlying principle behind each concept to help take your understanding of the inner workings of PowerShell to the next level.
As you are getting started with PowerShell, you will probably make use of PowerShell’s formatting cmdlets almost from the beginning. The formatting cmdlets are a key part of PowerShell’s functionality, as they enable you to organize the output of other cmdlets in a useful manner. This section provides an overview of the native PowerShell formatting cmdlets and their capabilities, explores how the formatting cmdlets actually perform their work, and discusses how to customize the output of the formatting cmdlets to meet your own requirements.
There are four native PowerShell cmdlets that deal specifically with formatting the output of commands:
• Format-List
—The Format-List
cmdlet formats the output of a command as a list of properties in which each property is displayed on a separate line. You can use Format-List
to format and display all or selected properties of an object as a list (Format-List *
).
• Format-Table
—The Format-Table
cmdlet formats the output of a command as a table with a selected properties of the object in each column. The object type determines the default layout and properties that are displayed in each column, but you can use the Property
parameter to select the properties that you want to see.
• Format-Wide
—The Format-Wide
cmdlet formats objects as a wide table that displays only one property of each object. You can use the Property
parameter to determine which property is displayed.
• Format-Custom
—The Format-Custom
cmdlet formats the output of a command as defined in an alternate view. Format-Custom
is designed to display views that are not just tables or just lists. You can use the views defined in the *format.PS1XML
files in the Windows PowerShell directory or you can create your own views in new PS1XML
files and use the Update-FormatData
cmdlet to add them to Windows PowerShell.
As you are first getting used to working with PowerShell, it is easy to type a command such as get-process | format-list
and appreciate the ease of getting results from PowerShell without needing to have a detailed understanding of exactly how PowerShell is displaying these results. This section takes a look at the mechanics of the formatting cmdlets and describes how to customize the output of PowerShell’s formatting cmdlets to meet your own requirements.
As we discussed in Chapter 3, “Advanced PowerShell Concepts,” the Extended Type System (ETS) enables existing .NET objects to be assigned new behaviors by PowerShell. PowerShell uses ETS to include detailed formatting information for the objects that it uses. This formatting information is stored in a series of files with a .ps1xml
extension that reside in the PowerShell home directory. In PowerShell 2.0 CTP2, the PowerShell home directory is C:WINDOWSsystem32WindowsPowerShellv1.0; this value is also stored in the PowerShell variable $PSHOME
. The command that follows shows a list of the .ps1xml
files that are part of the current release of PowerShell 2.0 CTP2.
The function of each of these seven formatting files is described in the following:
• Certificate.Format.ps1xml
—Provides formatting guidelines for objects such as X.509 certificates and certificate stores.
• DotNetTypes.Format.ps1xml
—Formats .NET types not covered by the other formatting files, such as CultureInfo, FileVersionInfo, and EventLogEntry objects.
• FileSystem.Format.ps1xml
—Contains formatting information for file system objects such as files and directories.
• Help.Format.ps1xml
—Describes the views that PowerShell uses to display help file content, such as detailed and full views, parameters, and examples.
• PowerShellCore.format.ps1xml
—Formats objects that are created by the PowerShell core cmdlets, such as Get-Member
and Get-ExecutionPolicy
.
• PowerShellTrace.format.ps1xml
—Controls the appearance of trace objects, such as those generated by the Trace-Command
cmdlet.
• Registry.format.ps1xml
—Instructs PowerShell on the formatting and appearance of registry objects, such as keys and entries.
As you continue to use PowerShell for your scripting automation tasks, you will likely run into situations where it would be really helpful to be able to add an additional property to a PSObject
. For example, you might want to create a new alias property to make it easier to reference a property in an existing PSObject
. There are two different ways to approach this kind of task. If you just need to add a property temporarily, you can use the Add-Member
cmdlet to add an additional property to an existing PSObject
. The sequence of commands that follows shows how to add a NoteProperty System.String
property called Status
with a value of done
to the type name System.IO.DirectoryInfo
:
The limitation of adding properties to PSObjects
interactively is that the properties persist only for the duration of your PowerShell session. After you close PowerShell, any custom properties that you have added are destroyed and need to be manually recreated in subsequent sessions if you want to use the properties again. If you find that you are making regular use of custom properties and would like to have these properties automatically loaded every time PowerShell is launched, the best solution is to create a custom .ps1xml
file that can be loaded as part of your PowerShell profile. The steps that follow describe how to create your own custom .ps1xml
file and add it to your Windows PowerShell console.
To create a new .ps1xml
file, you can begin with a blank template similar to the one shown in the following. You should save this file in your PowerShell user profile directory with the name Types.Custom.ps1xml
. To obtain the path for your PowerShell user profile, you can enter the command $profile
at any PowerShell prompt. The value that is returned is similar to C:Documents and Settings<username>My DocumentsWindowsPowerShellMicrosoft.PowerShell_profile.ps1
. Thus, this file should be saved as C:Documents and Settings<username>My DocumentsWindowsPowerShellTypes.Custom.ps1xml
.
The previous example is a valid .ps1xml
file, but it contains no data. To configure PowerShell to load this Types.Custom.ps1xml
file when PowerShell starts, you can add the following lines to your PowerShell profile:
At this point, you have configured PowerShell to load a custom .ps1xml
file named Types.Custom.ps1xml
for your user profile when PowerShell is launched. The next step is to add some customizations to Types.Custom.ps1xml
. The example that follows demonstrates adding an AliasProperty
named BytesFree
to the System.IO.DriveInfo PSObject,
which references the existing property AvailableFreeSpace
:
After you have saved the Types.Custom.ps1xml
file in your PowerShell user profile directory as described previously, exit PowerShell and restart it to allow the new custom types file to be included. The example that follows shows the output of [System.IO.DriveInfo]::getdrives()
pipelined to Format-List
(aliased here as fl
) with the properties Name
, DriveType
, VolumeLabel,
and BytesFree
. Note that PowerShell now accepts BytesFree
as a valid property for System.IO.DriveInfo
and returns the value for AvailableFreeSpace
, even though the BytesFree
label is displayed by Format-List
.
Although the example shown is a relatively straightforward demonstration of adding an alias for an existing PSObject
property, it is possible to perform much more detailed customizations in a .ps1xml
file, such as including script blocks to assign a scripted action to a PSObject
property. The following abbreviated example comes from PowerShell’s default types.ps1xml
file and illustrates how the DateTime
property of the System. DateTime
type is populated using a series of scripted commands to parse the input and return a value:
If you plan to make extensive customizations to your own .ps1xml
file, and especially if you plan to distribute your customizations to other users, it is a best practice to digitally sign your .ps1xml
file. Please review Chapter 5, “Understanding PowerShell Security,” for more information on digitally signing PowerShell files.
Most computer systems are used to store data, often in a structure such as a file system. Because of the amount of data stored in these structures, processing and finding information can be unwieldy. Most shells have interfaces, or providers, for interacting with data stores in a predictable, set manner. PowerShell also has a set of providers for presenting the contents of data stores through a core set of cmdlets. You can then use these cmdlets to browse, navigate, and manipulate data from stores through a common interface. To get a list of the core cmdlets, use the following command:
To view built-in PowerShell providers, use the following command:
The preceding list displays not only built-in providers, but also the drives each provider currently supports. A drive is an entity that a provider uses to represent a data store through which data is made available to the PowerShell session. For example, the Registry provider creates a PowerShell drive for the HKEY_LOCAL_MACHINE
and HKEY_CURRENT_USER
Registry hives.
To see a list of all current PowerShell drives, use the following command:
One way to access PowerShell drives and their data is with the Set-Location
cmdlet. This cmdlet, shown in the following example, changes the working location to another specified location that can be a directory, subdirectory, location stack, or Registry location:
Next, use the Get-ChildItem
cmdlet to list the subkeys under the Windows
key:
Note that with a Registry drive, the Get-ChildItem
cmdlet lists only the subkeys under a key, not the actual Registry values. This is because Registry values are treated as properties for a key rather than a valid item. To retrieve these values from the Registry, you use the Get-ItemProperty
cmdlet, as shown in this example:
As with the Get-Process
command, the data returned is a collection of objects. You can modify these objects further to produce the output you want, as this example shows:
Accessing data from a FileSystem
drive is just as simple. The same type of command logic is used to change the location and display the structure:
What’s different is that data is stored in an item instead of being a property of that item. To retrieve data from an item, use the Get-Content
cmdlet, as shown in this example:
PowerShell drives can be created and removed, which is handy when you’re working with a location or set of locations frequently. Instead of having to change the location or use an absolute path, you can create new drives (also referred to as “mounting a drive” in PowerShell) as shortcuts to those locations. To do this, use the New-PSDrive
cmdlet, shown in the following example:
To remove a drive, use the Remove-PSDrive
cmdlet, as shown here:
A PowerShell profile is a saved collection of settings for customizing the PowerShell environment. There are four types of profiles, loaded in a specific order each time PowerShell starts. The following sections explain these profile types, where they should be located, and the order in which they are loaded.
The All Users profile is located in %windir%system32windowspowershellv1.0profile.ps1
. Settings in the All Users profile are applied to all PowerShell users on the current machine. If you plan to configure PowerShell settings across the board for users on a machine, then this is the profile to use.
This profile is located in %windir%system32windowspowershellv1.0
ShellID
_profile.ps1
. Settings in the All Users host-specific profile are applied to all users of the current shell (by default, the PowerShell Console). PowerShell supports the concept of multiple shells or hosts. For example, the PowerShell Console is a host and the one most users use exclusively.
While the word host is colloquially used to refer to a physical computer system that provides services to users, in the context of the PowerShell language, the word host signifies an instance of the PowerShell console host program, which provides PowerShell services to users of PowerShell. Many of the native PowerShell cmdlets reflect this frame of reference, such as Read-Host and Write-Host, which both provide text processing functionality within an existing PowerShell session.
However, other applications can call an instance of the PowerShell runtime to access and run PowerShell commands and scripts. An application that does this is called a hosting application and uses a host-specific profile to control the PowerShell configuration. The host-specific profile name is reflected by the host’s ShellID
. In the PowerShell Console, the ShellID
is the following:
Putting this together, the PowerShell Console’s All Users host-specific profile is named Microsoft.PowerShell_profile.ps1
. For other hosts, the ShellID
and All Users host-specific profile names are different. For example, the PowerShell Analyzer (www.powershellanalyzer.com) is a PowerShell host that acts as a rich graphical interface for the PowerShell environment. Its ShellID
is PowerShellAnalyzer.PSA
, and its All Users host-specific profile name is PowerShellAnalyzer.PSA_profile.ps1
.
This profile is located in %userprofile%My DocumentsWindowsPowerShellprofile.ps1
. Users who want to control their own profile settings can use the current user’s profile. Settings in this profile are applied only to the user’s current PowerShell session and don’t affect any other users.
This profile is located in %userprofile%My DocumentsWindowsPowerShell
ShellID
_profile.ps1
. Like the All Users host-specific profile, this profile type loads settings for the current shell. However, the settings are user-specific.
A scope is a logical boundary in PowerShell that isolates the use of functions and variables. Scopes can be defined as global, local, script, and private. They function in a hierarchy in which scope information is inherited downward. For example, the local scope can read the global scope, but the global scope can’t read information from the local scope. Scopes and their use are described in the following sections.
As the name indicates, a global scope applies to an entire PowerShell instance. Global scope data is inherited by all child scopes, so any commands, functions, or scripts that run make use of variables defined in the global scope. However, global scopes are not shared between different instances of PowerShell.
The following example shows the $Processes
variable being defined as a global variable in the ListProcesses
function. Because the $Processes
variable is being defined globally, checking $Processes.Count
after ListProcesses
completes returns a count of the number of active processes at the time ListProcesses
was executed.
In PowerShell, you can use an explicit scope indicator to determine the scope a variable resides in. For instance, if you want a variable to reside in the global scope, you define it as $Global:
variablename
. If an explicit scope indicator isn’t used, a variable resides in the current scope for which it’s defined.
A local scope is created dynamically each time a function, filter, or script runs. After a local scope has finished running, information in it is discarded. A local scope can read information from the global scope but can’t make changes to it.
The following example shows the locally scoped variable $Processes
being defined in the ListProcesses
function. After ListProcesses
finishes running, the $Processes
variable no longer contains any data because it was defined only in the ListProcesses
function. As you can see, checking $Processes.Count
after the ListProcesses
function is finished produces no results.
A script scope is created whenever a script runs and is discarded when the script finishes running. To see an example of how a script scope works, create the following script and save it as ListProcesses.ps1
:
After you have created the script file, run it from a PowerShell session. Your output should look similar to this example:
Notice that when the ListProcesses.ps1
script runs, information about the first process object in the $Processes
variable is written to the console. However, when you try to access information in the $Processes
variable from the console, an error is returned because the $Processes
variable is valid only in the script scope. When the script finishes running, that scope and all its contents are discarded.
A private scope is similar to a local scope, with one key difference: Definitions in the private scope aren’t inherited by any child scopes.
The following example shows the privately scoped variable $Processes
defined in the ListProcesses
function. Notice that during execution of the ListProcesses
function, the $Processes
variable isn’t available to the child scope represented by the script block enclosed by { and } in lines 6–9.
This example works because it uses the ‘&'
call operator. With this call operator, you can execute fragments of script code in an isolated local scope. This technique is helpful for isolating a script block and its variables from a parent scope or, as in this example, isolating a privately scoped variable from a script block.
What if you want to use a script in a pipeline or access it as a library file for common functions? Normally, this isn’t possible because PowerShell discards a script scope whenever a script finishes running. Luckily, PowerShell supports the dot sourcing technique, a term that originally came from UNIX. Dot sourcing a script file tells PowerShell to load a script scope into the calling parent’s scope, rather than creating a new local scope for the execution of the script.
To dot source a script file, simply prefix the script name with a period (dot) when running the script, as shown here:
In PowerShell, library files are groups of cmdlets that are used to perform related functions. In most cases, library files that are used in PowerShell are implemented in a dynamic link library (DLL), which is then registered with PowerShell to provide access to the cmdlet functionality implemented in the DLL. This section walks through the process of registering a new PowerShell library file, adding the snapin to the console, and confirming that the new library file is providing the expected functionality.
By default, a number of PSSnapins
are included with PowerShell. These PSSnapins
contain the built-in cmdlets used by PowerShell. You can display a list of these cmdlets by entering the command Get-PSSnapin
at the PowerShell command prompt as shown in the following:
You can also use the Get-PSSnapin
command to return a list of all the registered PSSnapins
outside of the default PowerShell snapins listed in the previous example. Entering the command Get-PSSnapin- Registered
on a newly installed PowerShell system will return nothing, as shown in the following:
To register a third-party library, the .NET utility InstallUtil.exe
is used. In the example below, InstallUtil.exe
is used to install a third-party library file called nivot.powershell.eventing.dll
. This DLL is part of the third-party PowerShell Eventing library, which can be freely downloaded from http://www.codeplex.com/PSEventing:
The version of the InstallUtil
program that you must use varies depending on whether you are installing on a 32-bit or 64-bit platform.
To install 32-bit registry information, use: %systemroot%Microsoft.NETFrameworkv2.0.50727installutil.exe.
To install 64-bit registry information, use: %systemroot%Microsoft.NETFramework64v2.0.50727installutil.exe.
After the DLL library file has been registered with PowerShell, the next step is to register the DLL’s snapin with PowerShell, so that the cmdlets contained in the DLL are made available to PowerShell. In the case of the PowerShell Eventing library, the snapin is registered by using the command Add-PSSnapin pseventing
as shown in the following:
Now that the pseventing
snapin has been registered, you can enter the command Get-Help pseventing
as shown in the following to review the usage information for the PSEventing
cmdlets:
Now that the registration of the PSEventing
library DLL is complete and the associated snapin has been added to the console, you can enter the command Get-PSSnapin–registered
again and see that the PSEventing
snapin has been added to the console.
The PSEventing
library actually uses an installation script to complete the DLL registration and snapin tasks described previously. These tasks are listed out here simply to illustrate the process.
Now that you have registered the third-party library file and added its snapin to the console, you may find that the library does not meet your needs, and you want to remove it. The removal process is basically a reversal of the installation steps listed previously. First, you remove the snapin from the console using the command Remove-PSSnapin pseventing,
as shown in the following:
After the third-party snapin has been unregistered, you will once again use InstallUtil.exe
with a /U
switch to unregister the DLL, as shown in the following:
After the uninstall has completed, you can verify that the library file was successfully unregistered by entering the command Get-PSSnapin –registered
and verifying that no third-party libraries are listed.
As you can see from the examples above, PowerShell’s implementation of library files provides a straightforward method for extending PowerShell’s functionality, and is currently used by many third-party vendors to register their cmdlets. Although developing your own library files is beyond the scope of this chapter, there are many resources available online to assist you with this type of project. A good starting point for PowerShell development work is the Windows PowerShell Programmer’s Guide at http://msdn.microsoft.com/en-us/library/cc136167(VS.85).aspx.
This chapter covered a number of key PowerShell concepts that are useful as your knowledge of PowerShell increases. We discussed PowerShell’s formatting cmdlets and went into detail about how these cmdlets do their work, including a walkthrough of the Add-Member
cmdlet and creating a customized .ps1xml
file to load customized alias properties when PowerShell starts up. This chapter also covered PowerShell’s implementation of providers, which enable access to the contents of data stores through a core set of cmdlets. The provider cmdlets enable users to browse, navigate, and manipulate data from stores such as file systems, the Windows registry, certificate stores, and PowerShell variables. We reviewed PowerShell’s hierarchy of profiles and provided details on the functionality provided by each of the four profile types. The behavior of PowerShell scopes was documented with particular attention to the limits imposed by global, local, script, and private scopes. The dot sourcing technique was reviewed as a way to run a script in the current scope instead of creating a local scope. Finally, PowerShell’s use of library files was discussed, including a walkthrough of the process of registering a new third-party DLL with PowerShell and adding the associated snapin to the console, then removing the snapin and unregistering the DLL to return the system to its original state.