PowerShell Pipeline

Working with Object Types in PowerShell

Understanding what's going on in the background of your code will up your efficiency in PowerShell.

Whenever you use PowerShell, whether it is querying data or making a change to some configuration or existing object, you are working with different types of objects. You may not be aware of it because all of the work is happening behind the scenes in the script, but everything that you query or work with is an object such as a string or a larger object with multiple properties that may have other nested objects within it.

Let's look at the simplest of the object types, the string. Something simple such as "Hello World", when sent to Get-Member, shows up as a System.String.

"Hello world" | Get-Member 
Figure 1.

I'm not too interested today in looking at all of the methods and properties of the object itself, just the object type. If I do the same thing with a number such as 9, then we would see the object type come back as a System.Int32.

What if we had used "9" instead and sent it to Get-Member? Would it be an Int32 or would it become a string instead? Let's take a quick look and see what happens.

PS C:\> "9" | Get-Member

   TypeName: System.String

We end up seeing our "9" as a string because of the quotes that we used around it. When you import data from a file such as a CSV or a TXT document, there will be numbers that perhaps you would like to sort on, but because they are a string, it becomes pretty inaccurate. The same goes if you are trying to add string values of the numbers as shown below.

PS C:\> "9" + "9"
99

Last time I checked 9+9 is 18, not 99. Because they are strings, PowerShell will instead append the string data instead of actually adding the values together like we would have expected. Instead we would need to use a technique called "casting" to convert the string values into actual integers. We have a couple of approaches to make this work.

The first technique is to use the type operator to cast each of the strings to an integer.

PS C:\> [int]"9" + [int]"9"
18

This approach may seem familiar and if it does, it is because this is commonly used when working with parameters in functions and scripts.

Param (
[string]$Name,
[int]$Age,
[datetime]$Birthdate
)

By explicitly assigning a variable as a specific type, you can prevent improper data being assigned to the parameter. This same approach can be applied to your day to day variables to provide the same type of data protection.

[int]$Item  = $Null
$Item = 'test'
[Click on image for larger view.] Figure 2.


$Item = 8
$Item

Figure 3.

The other technique is to make use of the –as operator to convert an object from one type to another.

 

PS C:\> "9" -as [int] | Get-Member

 

   TypeName: System.Int32

If you have ever worked with Read-Host, you may have found that the data being returned is always a string regardless of what was typed at the prompt, such as a date or number. The trick to enforcing the data is to use a type operator to enforce the expected type.

[datetime]$Date  = Read-Host  'Enter a date (dd/mm/yyyy)'
$Date.GetType().Fullname
Figure 4.

Trying to force a value which is not of the datetime type will throw an error.

[Click on image for larger view.] Figure 5.

Types are not just for enforcing a particular kind of object in your scripts or making adjustments to allow data to play nice together. It can also be used for validation of data as well such as the case when working with IP addresses. Regular expressions are a great way to provide validation of an IP addresses but can become very complex depending on the level of validation. A simple RegEx might just test for the 4 octets but doesn't take into consideration invalid IPs like 330.123.45.222. Using a type operator of [ipaddress] helps to mitigate this issue by ensuring that the data presented is always a valid IP, otherwise an error will be thrown.

 

PS C:\> [ipaddress]'192.168.1.1'

Address            : 16885952
AddressFamily      : InterNetwork
ScopeId            :
IsIPv6Multicast    : False
IsIPv6LinkLocal    : False
IsIPv6SiteLocal    : False
IsIPv6Teredo       : False
IsIPv4MappedToIPv6 : False
IPAddressToString  : 192.168.1.1

 

PS C:\> [ipaddress]'345.192.56.78'
Cannot convert value "345.192.56.78" to type "System.Net.IPAddress". Error: "An invalid IP address was
specified."
At line:1 char:1
+ [ipaddress]'345.192.56.78'
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastParseTargetInvocation

PS C:\> [ipaddress]'2001:db8:a0b:12f0::1'

 

Address            :
AddressFamily      : InterNetworkV6
ScopeId            : 0
IsIPv6Multicast    : False
IsIPv6LinkLocal    : False
IsIPv6SiteLocal    : False
IsIPv6Teredo       : False
IsIPv4MappedToIPv6 : False
IPAddressToString  : 2001:db8:a0b:12f0::1

 

That is all today about working with object types in PowerShell. You can now take this knowledge and hopefully use it in your scripts to provide enforcement of variables and cast objects to their new types.

About the Author

Boe Prox is a Microsoft MVP in Windows PowerShell and a Senior Windows System Administrator. He has worked in the IT field since 2003, and he supports a variety of different platforms. He is a contributing author in PowerShell Deep Dives with chapters about WSUS and TCP communication. He is a moderator on the Hey, Scripting Guy! forum, and he has been a judge for the Scripting Games. He has presented talks on the topics of WSUS and PowerShell as well as runspaces to PowerShell user groups. He is an Honorary Scripting Guy, and he has submitted a number of posts as a to Microsoft's Hey, Scripting Guy! He also has a number of open source projects available on Codeplex and GitHub. His personal blog is at http://learn-powershell.net.

comments powered by Disqus
Most   Popular