Showing posts with label splat. Show all posts
Showing posts with label splat. Show all posts

Sunday, August 19, 2012

Splat in the Hat

In PowerShell the @ character is called the splat operator.  I'm still trying to figure out why it is called splat.
I'm pretty sure that's not why.  Nevertheless the splat operator has many uses in PowerShell.

In a previous post I demonstrated using the splat operator to create a custom object.

$X = New-Object PSObject -Properties @{
     Name="ObjectName";
     SomeValue=1;
     AnotherValue=10
}

The splat operator is also used to define an array.

$Array = @("one","two","three","four")

PowerShell is forgiving and will let you define an array without the splat operator.

$Array = "one","two","three","four"

But if you need to force the results to be an array you can use the splat operator.  I frequently do this if I'm creating an array of custom objects.  I first define an empty array, then fill it with the objects.

$Procs = @()
foreach ($P in (get-process | get-unique)) {
   $New = New-Object PSObject -Property @{
      Name=$P.ProcessName;
      Company=$P.Company;
      Product=$P.Product;
      Version=$P.ProductVersion
   }
   $Procs += $New
}
$Procs



The splat operator can also be used to force the results of a command into an array.  Enclose the command or cmdlet in parentheses and precede the opening parenthesis with the splat operator.
In the above example the variable $X has a length of 843 because it measures the length of the string returned by the Dir command.  $Y and $Z have a length of 1 because it is the length of the array.  The length of the first and only element of those single element arrays matches the length of the string $X.  I included the variable $Z to show another way of forcing a variable to be an array.

The splat operator is also used to create hash tables.  Hash tables are similar to dictionary objects in VBScript but are much more versatile.  A basic hash table lets you add and remove members in addition to doing searches.  To create the hash table start with the splat, enclose the table in curly braces, and separate items with semi-colons.

$Meals = @{"Breakfast"="Eggs";"Lunch"="Sandwich";"Supper"="Chicken"}


Another use for hash tables (and by association our friend the splat operator) is to define a group of parameters for cmdlets.  In this use the keys are the named parameters and the values are the input for the parameters.

Notice that when the hash table is used to provide parameters to the cmdlet the variable name is preceded with the splat operator instead of the dollar sign.  This tells PowerShell to process the variable as a hash table and use the keys and values as parameters to the cmdlet.

This technique is really useful for making code more readable.  The cmdlet Write-EventLog lets you add records to the Windows event logs.  In the example below I use the source WSH because its events have free form messages which allows me to use Get-WinEvent to confirm the results.

Write-EventLog -LogName Application -Source WSH -EventID 0 -EntryType Information -Message "I added this to the event log from PowerShell"


That works but the Write-EventLog cmdlet gets cumbersome with all of the parameters on a single line.  If I splat the parameters in a hash table the code becomes much easier to read and costs very little extra typing.

$ELParams = @{
    'LogName' = 'Application';
    'Source'  = 'WSH';
    'EventId' = 0;
    'EntryType'= 'Information';
    'Message' = 'I splatted this to the Application log using PowerShell'
}
Write-EventLog @ELParams


Note that while I hard coded all of the parameter names and values in the hash table I could just as easily use variables for any of them.  This allows for some sophisticated processing inside loops and user defined functions.

One final use for the splat operator is the Here-String.  A Here-String is PowerShell's facility for specifying a large block of text that spans multiple lines.  Start the bock of text with the splat and quote @" and end the block with a quote and splat "@.  (On a personal note "Quote and Splat" is the law firm who handled my divorce settlement.)  Inside the Here-String you can specify line breaks and include quotes and other special characters.  The Here-String will display just as you specified.


The splat operator gives us a variety of shortcuts to keep our code efficient and tidy.  But remember that with great power comes great responsibility.  So remember to splat wisely, unlike this juvenile.

Commentors:  Seriously, does anybody know why they call it splat?

Tuesday, May 29, 2012

I, Object

As discussed in the post on the pipeline, part of the power behind PowerShell is that the output from most cmdlets is an object or an array of objects.  And cmdlets are designed to process objects received from the pipeline.  Since PowerShell is built on the .Net framework everything is an object.

An object has properties that contain information about the object and is operated on using methods.  For example, I am an object and have properties such as my name and phone number.  To contact me you might use the method of calling my phone number and addressing me by name. 

You can see the properties and methods of an object by piping it through the Get-Member cmdlet.  Even a scalar such as a simple string is still an object.
My string variable has an attribute named length, the number of characters in the string, and a bunch of methods for slicing and dicing the string.

An array is an object that is a collection of objects.  If you pipe an array through Get-Member you get the properties and methods of the objects contained in the array, not the array itself.  Arrays are very accommodating that way since the attributes of the members are much more interesting.

Some objects have many of properties.  Usually you want to keep just the few of those properties so you will use the Select-Object cmdlet to create the subset.

But what if I need to go in the opposite direction?  What if I need to add some properties to an object?  The Add-Member cmdlet is the answer to your question.
This is my string from the previous example.  I use Add-Member to append a property to the string that contains the last character in the string. 

Sometimes it is handy to create an array of custom objects.  I use this technique when my goal is to use Export-CSV to create a spreadsheet of just the information requested by my PHB

$CSV = 'C:\Users\Administrator\Documents\DisabledUsers.csv'
$All = Get-QADUser -Disabled -SearchRoot 'OU=Locations,dc=toddp,dc=local' `
          -SearchScope Subtree


$Disabled = @()
foreach ($U in $All) {
    $U.Name
    $New = New-Object Object
    $New | Add-Member NoteProperty Name $U.name
    $New | Add-Member NoteProperty First $U.FirstName
    $New | Add-Member NoteProperty Last $U.LastName
    $New | Add-Member NoteProperty Full $U.DisplayName
    $DN = $U.DN.Split(',')
    $Site = $DN[$DN.Length-4].Substring(3)
    $New | Add-Member NoteProperty Site $Site
    $Disabled += $New
}
$Disabled | Export-Csv $CSV -NoTypeInformation -Encoding ASCII

In this example I'm using the Quest Active Directory cmdlet Get-QADUser to show some information on disabled user accounts.  First I create the empty array by assigning it the value @().  (In PowerShell the @ is called the splat operator.  Apparently the designers thought calling it "splunge" was a bit silly.)  I then use the New-PSObject cmdlet to create a custom object in my loop.  I use Add-Member to add properties for my report to the object, and then append the custom object to the array.  My test domain has an OU named Locations which contains several site OUs and within those site OUs there are OUs for users and computers in the site.  So I parse the DN (distinguished name) property of the user account to determine the site name.

That works but it looks kind of ugly and requires too much typing.  In PowerShell 2 the New-Object cmdlet has been revamped to allow a technique called "splatting".  So instead of a bunch of Add-Member statements you assign all of the properties in a script bock that is preceded by the splat operator.

$CSV = 'C:\Users\Administrator\Documents\DisabledUsers2.csv'
$All = Get-QADUser -Disabled -SearchRoot 'OU=Locations,dc=toddp,dc=local' `
         -SearchScope Subtree


$Disabled = @()
foreach ($U in $All) {
   $U.Name
   $DN = $U.DN.Split(',')
   $Site = $DN[$DN.Length-4].Substring(3)
   $New = New-Object PSObject -Property @{
       Name  = $U.name;
       First = $U.FirstName;
       Last  = $U.LastName;
       Full  = $U.DisplayName;
       Site  = $Site
   }
   $Disabled += $New
}

$Disabled | Export-Csv $CSV -NoTypeInformation -Encoding ASCII

That saves me some typing and it does improve legibility.  But splatting doesn't respect the order of my properties:

So if I need the properties in a specific order I use the Select-Object cmdlet:

$Enabled = $Enabled | Select-Object name,First,Last,Full,Site
$Enabled | Export-Csv $CSV -NoTypeInformation -Encoding ASCII

Which gives us the output like the first example:

This technique gets even easier in PowerShell 3 which includes the object type [PSCustomObject].  Shay Levy goes into detail on how this will work once PowerShell 3 is released.  For my example the script in PowerShell 3 would look like:

$CSV = 'C:\Users\Administrator\Documents\DisabledUsers2.csv'
$All = Get-QADUser -Disabled -SearchRoot 'OU=Locations,dc=toddp,dc=local' `
         -SearchScope Subtree


$Disabled = @()
foreach ($U in $All) {
   $U.Name
   $DN = $U.DN.Split(',')
   $Site = $DN[$DN.Length-4].Substring(3)
   $Disabled += [PSCustomObject]@{
       Name  = $U.name
       First = $U.FirstName
       Last  = $U.LastName
       Full  = $U.DisplayName
       Site  = $Site
   }
}

$Disabled | Export-Csv $CSV -NoTypeInformation -Encoding ASCII

Not only does this save me even more typing, but the [PSCustomObject] will preserve the order of the properties and perform more efficiently than the New-Object cmdlet.

There is no need to object to PowerShell objects.  You can augment and condense the objects you get from cmdlets and even create your own objects.  Just another example of the awesome power of PowerShell.

Commentors: How else do you use custom objects to improve your efficiency? 

--updated 26 Jun 2012 to correct formatting--