Windows Powershell The Power of Variables

Don Jones

If you work with a Windows-based scripting language, such as VBScript or KiXtart, you're accustomed to variables being nothing more than a sort of storage mechanism for data. Windows PowerShell has variables, too, but they're much more powerful than the variables in older scripting languages. The Windows

PowerShell variables are actually mapped to underlying classes in the Microsoft® .NET Framework. And in the Framework, variables are objects, meaning they can store data and also manipulate it in many ways. In fact, the robust capabilities of variables in Windows PowerShell™ are the reason why the Windows PowerShell scripting language doesn't contain any intrinsic data manipulation functions. It doesn't need these functions since the variables themselves already provide the functionality.

Declaring Variables

Although the New-Variable cmdlet in Windows PowerShell does allow you to declare a variable and assign an initial value to it, you don't have to use the cmdlet. Instead, you can create a new variable on the fly simply by assigning a value to it:

$var = "Hello"

In Windows PowerShell, variable names always start with a dollar sign ($) and can contain a mix of letters, numbers, symbols, or even spaces (though if you use spaces, you need to enclose the variable in braces, such as ${My Variable} = "Hello"). This example created a new variable named $var and assigned it the initial value of "Hello". Because in this case the value is a string of characters, Windows PowerShell will use the String data type to store the value. In .NET Framework terms, that's the System.String class, which has perhaps the most built-in functionality of any variable type. If, say, I want to see an all-lowercase version of the value in $var, I can do this:

PS C:\> $var.ToLower()
hello
PS C:\>

The ToLower method is built into the System.String class, and it produces an all-lowercase representation of the string's value. It doesn't change the actual contents of the variable $var, though. To see a complete list of everything the System.String class is capable of doing, pipe a string variable to the Get-Member cmdlet, like this:

$var | get-member

Figure 1 shows the output, which includes a couple dozen methods used for manipulating strings. Nearly all the functionality provided by VBScript string-manipulation functions is provided instead by methods of the string variable in Windows PowerShell.

Figure 1 A look at the System.String class output

Figure 1** A look at the System.String class output **(Click the image for a larger view)

Many of the string variable's capabilities are more useful in an administrative context than the string functions in a language like VBScript. Suppose you've written a script that reads in Universal Naming Convention (UNC) paths from a file and does something with them. You'd want to verify that each path read in is a UNC before attempting to use the path. The StartsWith method lets you confirm that a string's value starts with the necessary backslash characters a UNC requires:

PS C:\> $path = "\\Server\Share"
PS C:\> $path.StartsWith("\\")
True
PS C:\>

Because StartsWith returns a True or False value, you can use it in logical constructs:

if ($path.StartsWith("\\")) {
 # code goes here
}

Windows PowerShell even provides a form of autocompletion for variables' methods, reducing the amount of typing you have to do. If $var contains a string, you can type

$var. 

and then press Tab, which pops up the first method name for the $var variable. Pressing Tab again moves to the next method, and pressing Shift+Tab brings up the prior method. In this fashion, you can cycle through all the available methods until you find the one you want.

Variable Confusion

So far, my examples have allowed Windows PowerShell to determine the data type of my variables. Assigning a string to a variable essentially forces the variable to be of the System.String class. Assigning a number to a variable, on the other hand, usually results in the variable becoming an Integer (or, more specifically, an Int32, which can store a specific range of values). Consider this, for example:

PS C:\> $int = 5
PS C:\> $int | get-member

  TypeName: System.Int32

This truncated output shows that Windows PowerShell is treating $int as an Int32, which has its own set of methods and properties. And, in fact, its set of methods and properties are far fewer than those of the String type.

$int was made an Int32 because the value wasn't enclosed in quotes and because the value was composed solely of digits. Had it been in quotes, it would have been interpreted as a System.String.

Allowing Windows PowerShell to decide what data type to use won't always produce the results you want. Suppose you're reading values out of a file and you always want the values to be treated as strings. Some of the values, however, might contain only digits, raising the possibility that Windows PowerShell would treat them as Int32 or another numeric type. That may cause problems for your script. If Windows PowerShell doesn't recognize the value as a string, then all the methods of the System.String class aren't available (and your script might rely on one of these unavailable methods).

To work around this, you can force Windows PowerShell to treat a variable as a certain type by declaring that type when you first use the variable. Here's an example:

[string]$var = 5

Normally, $var would have been an Int32, but here I've forced Windows PowerShell to make $var a String, ensuring I can use all the methods associated with the System.String class. The variable type declaration also provides a handy kind of self-documentation to the code, since it's now obvious what type of data is expected to go into $var. In fact, I've gotten into the habit of always declaring a specific type for all of my variables, completely taking the decision away from Windows PowerShell. This makes the behavior of my scripts more predictable and in several instances has saved me quite a bit of debugging time.

As you'd expect, forcibly declaring variables does have repercussions, though they're not necessarily bad. Take this example, which doesn't declare the variable's type:

PS C:\> $me = 5
PS C:\> $me = "Don"

The $me variable was initially an Int32, but Windows PowerShell changed it to a String when the value "Don" was added. Windows PowerShell can change the type of a variable as needed, provided the variable hasn't already been explicitly set to a specific type.

In this example, I forced $me to be an Int32 using the [int] type name:

PS C:\> [int]$me = 5
PS C:\> $me = "Don"
Cannot convert value "Don" to type 
"System.Int32". Error: "Input string 
was not in a correct format."
At line:1 char:4
+ $me <<<< = "Don"

Then, when I tried to assign a string value to it, an error message was displayed. Since I'd explicitly forced $me to be an Int32, Windows PowerShell expected to convert the string "Don" into an integer value. It was unable to do this, nor was it able to change the type of $me to String.

So Many Types

There are many types available within Windows PowerShell. In fact, you can use any .NET Framework type (and the available types number in the hundreds). However, Windows PowerShell defines several shortcuts for common types of data. Figure 2 is just a partial list, showing 10 of the most commonly used type shortcuts. For a complete list, refer to the Windows PowerShell documentation.

Figure 2 Common type shortcuts

Shortcut Data Type
[datetime] Date or time
[string] String of characters
[char] Single character
[double] Double-precision floating number
[single] Single-precision floating number
[int] 32-bit integer
[wmi] Windows Management Instrumentation (WMI) instance or collection
[adsi] Active Directory Services object
[wmiclass] WMI class
[Boolean] True or False value

You can also declare a variable's type using the full .NET Framework class name, like so:

[System.Int32]$int = 5

This technique allows you to use types that are in the .NET Framework, but that don't have a specific shortcut in Windows PowerShell.

Extending Types

Perhaps the coolest thing about Windows PowerShell is its ability to extend the capabilities of these variable types. In the Windows PowerShell installation folder (normally in %systemroot\system32\windowspowershell\v1.0, though you'll find that the path is somewhat different on 64-bit systems), you'll find a file called types.ps1xml. You can edit this file in Notepad or in an XML editor, such as PrimalScript. By default, the System.String class isn't listed, although many other types are. However, I can add a new capability to the System.String type by adding this to the types.ps1xml file.

After making the change, I need to close and reopen Windows PowerShell. I also need to make sure that the local script execution policy allows unsigned scripts to execute since I haven't signed types.ps1xml:

Set-executionpolicy remotesigned

Now, once I restart Windows PowerShell, I can use the new functionality, like this:

PS C:\> [string]$comp = "localhost"
PS C:\> $comp.canping
True
PS C:\>

As you can see, I've added a CanPing property to the System.String class. This returns True or False to indicate whether the local computer is able to ping the address contained in the string. In Figure 3, you'll notice that the WMI query utilizes a special variable, $this. The $this variable represents the current value contained in the string and is how the content of the string variable is passed into the WMI query.

Figure 3 Extend System.String type

<Type>
  <Name>System.String</Name>
  <Members>
    <ScriptProperty>
      <Name>CanPing</Name>
      <GetScriptBlock>
      $wmi = get-wmiobject -query "SELECT *
FROM Win32_PingStatus WHERE Address = '$this'"
      if ($wmi.StatusCode -eq 0) {
        $true
      } else {
        $false
      }
      </GetScriptBlock>
    </ScriptProperty>
  </Members>
</Type>

Let a Variable Do the Work

Windows PowerShell provides flexible, feature-filled variable types and an equally flexible system for extending the functionality of types. These capabilities make it very easy to build a great deal of power into your scripts. In fact, variables can become a major building block in complex scripts, in many cases taking on the advanced capabilities you'd normally find in a more complex function.

Learn More Online

Join me for this series of webcasts, held on the second Tuesday of each month, where we'll continue to dive into the rich scripting functionality provided by Windows PowerShell. Visit microsoft.com/events/series/donjonesscripting.mspx to sign up today.

February 20, 2007 Windows PowerShell: The Scripting Crash Course

Join this webcast to learn about the variables, language constructs, scripts, and functions in Windows PowerShell. In this one-hour crash course, we demonstrate how the simple but effective scripting language in Windows PowerShell makes Windows® administrative automation faster. We also explore several third-party tools that make Windows PowerShell scripting as painless as possible.

March 20, 2007 Windows PowerShell: Functions, Filters, and Efficiency

Learn to create effective, modularized code in Windows PowerShell functions and filters. We explore the robust scoping rules in Windows PowerShell that enable you to fully encapsulate functions and filters and explain how you can easily reuse these functions and filters across projects. We also demonstrate how you can add your own custom functions into the global scope in Windows PowerShell, which makes it easy to assemble a library of useful utility functions at your fingertips.

April 17, 2007 Windows PowerShell and Windows Management Instrumentation

Windows PowerShell can access all the power and capability of Windows Management Instrumentation, or WMI. We describe not only how to use Windows PowerShell to access WMI, but also how to pass WMI objects and collections through the Windows PowerShell pipeline. We explore how to utilize WMI properties and methods in Windows PowerShell scripts and illustrate the underlying security and configuration features in WMI.

May 22, 2007 Windows PowerShell: Converting from VBScript

Want to convert your scripts—or even just your skills—from VBScript to Windows PowerShell? Join this webcast to learn how. We explore how Windows PowerShell includes all of the major constructs and features of VBScript, making it easy to translate your VBScript skills to this new administrative environment. We illustrate how to convert tools in VBScript into the native scripting language in Windows PowerShell. We also examine the unique structure in Windows PowerShell and show you how to start using the Windows PowerShell scripting language so that your efforts become more efficient and effective.

June 19, 2007 Windows PowerShell: Under-the-Hood Extensions

In this webcast, we explore how Windows PowerShell makes use of the powerful and flexible .NET Framework to handle data, giving you hundreds of built-in functions for managing strings, dates, and other types of data. But did you know that you can extend these functions using Windows PowerShell scripts? We show you how to make string variables that contain not only a computer name but can also tell you if that computer is up and running. Learn how to create date and time variables that can automatically format their data without the use of external functions. Join this session to see how to build all kinds of new functionality into Windows PowerShell in just minutes, making Windows administration faster and easier.

Don Jones is the Director of Projects and Services for SAPIEN Technologies, and coauthor of Windows PowerShell: TFM (SAPIEN Press). Contact Don at www.ScriptingAnswers.com.

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.