Variables as Objects


Earlier we mentioned that PowerShell variables are actually objects. In .NET, string variables are extremely robust, and have a number of methods and properties. Refer to Appendix B for a complete summary of the methods and properties. One object is Split, which is a method that takes a string and creates an array (or list) by breaking the list on some character such as comma or a space. Try this in PowerShell:

 PS C\:> "1,2,3,4".Split(",") 

This tells PowerShell to "take this string, and execute its Split method. Use a comma for the method's input argument." When PowerShell executes this commend, the method returns an array of four elements, each element containing a number. PowerShell displays the array it in a textual fashion, with one array element per line:

 1 2 3 4 

There are other ways to use this. For example, PowerShell has a cmdlet called Get-Member that displays the methods and variables associated with a given object instance. Taking a string like "Hello, World", which is a String object, and piping it to the Get-Member cmdlet, displays information about that String object:

 PS C:\> "Hello, World" | get-member    TypeName: System.String Name             MemberType            Definition ----             ----------            ---------- Clone            Method                System.Object Clone() CompareTo        Method                System.Int32 CompareTo(Object v Contains         Method                System.Boolean Contains(String CopyTo           Method                System.Void CopyTo(Int32 source EndsWith         Method                System.Boolean EndsWith(String Equals           Method                System.Boolean Equals(Object ob GetEnumerator    Method                System.CharEnumerator GetEnume GetHashCode      Method                System.Int32 GetHashCode() GetType          Method                System.Type GetType() GetTypeCode      Method                System.TypeCode GetTypeCode() get_Chars        Method                System.Char get_Chars(Int32 ind get_Length       Method                System.Int32 get_Length() IndexOf          Method                System.Int32 IndexOf(Char value IndexOfAny       Method                System.Int32 IndexOfAny(Char[] Insert           Method                System.String Insert(Int32 star IsNormalized     Method                System.Boolean IsNormalized(), LastIndexOf      Method                System.Int32 LastIndexOf(Char v LastIndexOfAny   Method                System.Int32 LastIndexOfAny Normalize        Method                System.String Normalize(), Syst PadLeft          Method                System.String PadLeft(Int32 tot PadRight         Method                System.String PadRight(Int32 to Remove           Method                System.String Remove(Int32 star Replace          Method                System.String Replace(Char oldC Split            Method                System.String[] Split(Params Ch StartsWith       Method                System.Boolean StartsWith(Strin Substring        Method                System.String Substring(Int32 s ToCharArray      Method                System.Char[] TSystem.Char[] To ToLower          Method                System.String ToLower(), Syste ToLowerInvariant Method                System.String ToLowerInvariant( ToString         Method                System.String ToString(), Syste ToUpper          Method                System.String ToUpper(), System ToUpperInvariant Method                System.String ToUpperInvariant( Trim             Method                System.String Trim(Params Char[ TrimEnd          Method                System.String TrimEnd(Params Ch TrimStart        Method                System.String TrimStart(Params Chars            ParameterizedProperty System.Char Chars(Int32 inde Length           Property              System.Int32 Length {get;} 

Even though this output is truncated a bit to fit in this book, you can see that it includes every method and property of the String. It also correctly identifies "Hello, World" as a "System.String" type, which is the unique type name that describes what we informally call a String object. You can pipe nearly anything to Get-Member to learn more about that particular object and its capabilities.

One of the most frequently used object variables is the $_ variable. We've used it repeatedly in our examples. $_ is an automatic variable that stands for the current object. Here's an example:

 PS C:\> get-process | where {$_.workingset -gt 10000*1024} Handles  NPM(K)    PM(K)      WS(K) VS(M)   CPU(s)     Id ProcessName -------  ------    -----      ----- -----   ------     -- -----------     604     14    23856      37308   112     32.26   2236 explorer     114      5     6540      12380    52      3.12   2444 i_view32     617     10    56252      52784   172      7.02    632 PS     246      5     8672      12300    45      3.02   1512 MsMpEng     201     12    11528      19400    71      1.24   3452 Skype     198      6    26016      12816  1584      1.07    544 sqlservr    1416     49    17768      26312   131      5.25   1556 svchost     405     14    37140      58236   287    111.07   3900 WINWORD 

This pipes the output of Get-Process to the Where cmdlet and only displays those processes where the workingset property of the current object ($_) is greater than 10240000. We use dotted notation to define the object and property: $_.workingset.

How Did We Know That?

The output of the Get-Process cmdlet is a System.Diagnostic.Process object. One of this object's properties is workingset. You can find the properties yourself by running:

 PS C:\> get-process |get-member -membertype property 

Let's go back and look at the array of service objects we created earlier in this chapter. We used the following expression to create the object:

 PS C:\> $svc=get-service | where {$_.status -eq "running"} 

Since we know the array contains service objects, it might help to know more about these objects. Let's look at the first element of the array:

 PS C:\> $svc[0] Status   Name               DisplayName ------   ----               ----------- Running  AudioSrv           Windows Audio 

We know the first running service in the array is Windows Audio. We're going to learn about this object using this service as an example even if we're not interested in that specific service. We'll pass the first element of the array to Get-Member and display the object's properties.

 PS C:\> $svc[0] |get-member -membertype properties    TypeName: System.ServiceProcess.ServiceController Name                      MemberType    Definition ----                      ----------    ---------- Name                      AliasProperty Name = ServiceName CanPauseAndContinue       Property      System.Boolean CanPauseAndCont CanShutdown               Property      System.Boolean CanShutdown {ge CanStop                   Property      System.Boolean CanStop {get;} Container                 Property      System.ComponentModel.IContain DependentServices         Property      System.ServiceProcess.ServiceC DisplayName               Property      System.String DisplayName {get MachineName               Property      System.String MachineName {get ServiceHandle             Property      System.Runtime.InteropServices ServiceName               Property      System.String ServiceName {get ServicesDependedOn        Property      System.ServiceProcess.ServiceC ServiceType               Property      System.ServiceProcess.ServiceT Site                      Property      System.ComponentModel.ISite Si Status                    Property      System.ServiceProcess.ServiceC 

Every property is not necessarily populated, nor may they all be of interest. However, we want to show you where this information comes from and how it is connected to the object.

The easier way to learn property names is by sending the output of the array element to Format-List:

 PS C:\> $svc[0] |format-list Name                : AudioSrv DisplayName         : Windows Audio Status              : Running DependentServices   : {} ServicesDependedOn  : {RpcSs, PlugPlay} CanPauseAndContinue : False CanShutdown         : False CanStop             : True ServiceType         : Win32ShareProcess 

Variable Types

The fact that PowerShell is built on .NET gives it tremendous versatility that isn't always obvious. For example, in Chapter 1, we explained that any PowerShell variable can contain any type of data. This is true because all types of data such as strings, integers, and dates are .NET classes, which means they all inherit from the base class named Object. A PowerShell variable can contain anything that inherits from Object. However, as you saw in earlier examples with a string, PowerShell can tell the difference between different classes that inherit from Object.

You can force PowerShell to treat objects as a more specific type. When you do this, you are asking PowerShell to cast a variable to a specific type. For example, take a look at this sequence:

 PS C:\> $one = 5 PS C:\> $two = "5" PS C:\> $one + $two 10 PS C:\> $one = 5 PS C:\> $two = "5" PS C:\> $one + $two 10 PS C:\> $two + $one 55 

Here we give PowerShell two variables. One variable contains the number five, while the other variable contains the string character "5." This might look the same to you, but this is a big difference to a computer! Since we didn't specify what type of data these variables are, PowerShell assumes they are both the generic Object type. This caused PowerShell to decide it would figure out something more specific when the variables are used.

When we added $one and $two or 5 + "5," PowerShell said, "Aha, this must be addition. The first character is definitely not a string because it's not in double quotes. The second character is in double quotes but Well, if I take the quotes away it looks like a number, so I'll add them." PowerShell logic correctly gave ten as the result.

However, when we add $two and $one, reversing the order, PowerShell has a different decision to make. In this case PowerShell said, "I see addition, but this first operand is clearly a string. The second one is a generic Object, so let's also treat it like a string, and concatenate the two." This PowerShell logic gave us the string "55," which is just the first five tacked onto the second.

But what about:

 PS C:\> [int]$two + $one 10 

This is the same order as the example that resulted in "55." However, this time we specifically told PowerShell to cast the generic object in $two as an Int or integer, which is a type PowerShell knows. So, it used the same logic as in the first example. It added the two to come up with ten.

You can force PowerShell to treat anything as a specific type. For example:

 PS C:\> $int = [int]"5" PS C:\> $int | get-member    TypeName: System.Int32 Name        MemberType Definition ----        ---------- ---------- CompareTo   Method     System.Int32 CompareTo(Int32 value), System.Int Equals      Method     System.Boolean Equals(Object obj), System.Boole GetHashCode Method     System.Int32 GetHashCode() GetType     Method     System.Type GetType() GetTypeCode Method     System.TypeCode GetTypeCode() ToString    Method     System.String ToString(), System.String ToStrin 

Here the value "5" would normally be either a String object or, at best, a generic Object. However, by specifying the type [int], we forced PowerShell to try and convert "5" into an integer before storing it in the variable $int. The conversion was successful because we can see where we piped $int to Get-Member, which revealed the object's type: System.Int32.

Note that once you apply a specific type to a variable, it stays that way until you specifically change it. For example:

 [int]$int = 1 

This creates a variable named $int as an integer, and assigns it the value 1. The $int variable will be treated as an integer from now on, even if you don't include the type:

 $int = 2 

It is still using $int as an integer because it was already cast into a specific type. Once set up to be an integer, you can't put other types of data into it. Here's an example of an error because we tried to put a string into a variable that was already specifically cast as an integer:

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

However, you can recast a variable by reassigning a new, specific type:

 [string]$int = "Hello" 

That works fine, and $int will now be treated as a string by PowerShell.

PowerShell isn't a miracle worker. For example, PowerShell will complain if you try to force it to convert something that doesn't make sense:

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

This occurred because "Hello" can't sensibly be made into a number. The next example is even more fun since it illustrates some of the advanced data types:

 PS C:\> $xml = [xml]"<users><user name='joe' /></users>" PS C:\> $xml.users.user name ---- joe 

Here we created a string, but told PowerShell it was of the type XML, which is another data type with which PowerShell is familiar. XML data works sort of like an object in that we define a parent object named Users, and a child object named User. The child object has an attribute called Name, with a value of Joe. So, when we ask PowerShell to display $xml.users.user, it displays all the attributes for that user. We can prove that PowerShell treated $xml as XML data by using Get-Member:

 PS C:\> $xml | get-member    TypeName: System.Xml.XmlDocument Name                        MemberType            Definition ----                        ----------            ---------- ToString                    CodeMethod            static System.Stri add_NodeChanged             Method                System.Void add_No add_NodeChanging            Method                System.Void add_No add_NodeInserted            Method                System.Void add_No add_NodeInserting           Method                System.Void add_No add_NodeRemoved             Method                System.Void add_No add_NodeRemoving            Method                System.Void add_No AppendChild                 Method                System.Xml.XmlNode Clone                       Method                System.Xml.XmlNode CloneNode                   Method                System.Xml.XmlNode CreateAttribute             Method                System.Xml.XmlAttr CreateCDataSection          Method                System.Xml.XmlCDat CreateComment               Method                System.Xml.XmlComm CreateDocumentFragment      Method                System.Xml.XmlDocu CreateDocumentType          Method                System.Xml.XmlDocu CreateElement               Method                System.Xml.XmlElem CreateEntityReference       Method                System.Xml.XmlEnti CreateNavigator             Method                System.Xml.XPath.X CreateNode                  Method                System.Xml.XmlNode CreateProcessingInstruction Method                System.Xml.XmlProc ... 

This demonstrates not only that variables are objects, but also that PowerShell does understand different types of data, and provides different capabilities for them.

If you're curious about what object types are available, here's a quick list of more common types:

  • Array

  • Bool (Boolean)

  • Byte

  • Char (a single character)

  • Char[] (Character array)

  • Decimal

  • Double

  • Float

  • Int (Integer)

  • Int[] (Integer array)

  • Long (Long integer)

  • Long[] (Long integer array)

  • Regex (Regular expression)

  • Single

  • Scriptblock

  • String

  • WMI

  • XML

There are more types than those listed above. In fact, we'll be popping in with details on the other types as appropriate throughout this book. Some of them aren't frequently used in administrative scripting, so we don't want to arbitrarily hit you with all of them at once. Instead, we'll cover them in a context where they're used for something useful. Just remember that, unlike other scripting languages with which you may be familiar, a PowerShell variable can contain more than a number or string.

Variable Precautions

One thing to be careful of is PowerShell's ability to change the type of a variable if you haven't explicitly selected a type. For example:

 Write-host $a.ToUpper() 

This works fine if $a contains a string, as shown here:

 PS C:\> $a = "Hello" PS C:\> write-host $a.ToUpper() HELLO PS C:\> 

However, you'll get an error if $a was already set to an integer value:

 PS C:\> $a = 1 PS C:\> write-host $a.ToUpper() Method invocation failed because [System.Int32] doesn't contain a method named 'ToUpper'. At line:1 char:22 + write-host $a.ToUpper( <<<< ) PS C:\> 

This occurs because, as an integer, $a doesn't have a ToUpper() method. Since this type of error can be tricky to troubleshoot, you need to watch out for this when you're writing scripts that take input from other sources such as a user or a file.

One way around it is to force PowerShell to treat the variable as the string you're expecting it to be:

 PS C:\> $a = 1 PS C:\> $a = [string]$a PS C:\> write-host $a.ToUpper() 1 PS C:\> 

You don't necessarily need to select a type up front for every variable you use, but you should be aware of situations that can make a variable contain a type of data other than what you originally expected.

You should also take precautions with variable naming. PowerShell is pretty forgiving and will let you use just about anything as a variable name:

 PS C:\> $$="apple" PS C:\> $$ apple PS C:\> ${var}=100 PS C:\> ${var} 100 PS C:\> PS C:\> $7="PowerShell Scripting" PS C:\> $7 PowerShell Scripting PS C:\> 

However, if you attempt to create a variable with anything other than a number or letter, PowerShell will complain:

 PS C:\> $(j)="SAPIEN" Invalid assignment expression. The left hand side of an assignment operator nee ds to be something that can be assigned to like a variable or a property. At line:1 char:6 + $(j)=" <<<< SAPIEN" PS C:\> 

Using these types of variables names isn't very practical or recommended. Instead, you should use variable names that are meaningful to you. So instead of:

 $g=Get-Process 

Use something like:

 $Processes=Get-Process 

It may require a bit more typing, but using meaningful variable names will definitely make it easier to troubleshoot and maintain your PowerShell scripts.



Windows PowerShell. TFM
Internet Forensics
ISBN: 982131445
EAN: 2147483647
Year: 2004
Pages: 289

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net