Use PowerShell Hash Tables with Your Cmdlets

Use PowerShell Hash Tables with Your Cmdlets

  • Comments 7
  • Likes

Summary: Learn how to Use Windows PowerShell hash tables with your cmdlets to create powerful commands.

 

Hey, Scripting Guy! QuestionHey, Scripting Guy! I think I understand hash tables after having read your earlier articles about working with hash tables. What I do not understand is how hash tables work within Windows PowerShell commands. In addition, how do I find Windows PowerShell cmdlets that use hash tables?

—JB

 

Hey, Scripting Guy! AnswerHello JB,

Microsoft Scripting Guy Ed Wilson here. I spent the Fourth of July playing with Windows PowerShell. The Scripting Wife spent the holiday reading. We both had a great holiday, and we were able to do exactly what we wanted to do for three days—it was wonderful.

Note   This is the fourth (and last) blog post in a series of posts discussing the hash table. The first post covered the basics of working with hash tables such as creating a hash table, and adding and removing items from a hash table. The second post went into more detail and discussed programmatically adding items to the hash table, counting items in a hash table, and clearing the contents of the hash table. In the third post, I talked about using a hash table to filter lists by removing duplicate items. In today’s post, I discuss using hash tables with Windows PowerShell cmdlets.

One of the things I did was play around with my Get-Help trick of searching the help contents for specific words (I discussed this trick in the article on Monday in which I detailed the four matching rules that Get-Help utilizes.) To find all of the mentions of hash tables in help, I used several different commands. The commands and their associated output appear here (the command Get-Help Hash-Table is not shown here because I ran it on Monday and the output appears in that article).

PS C:\hsgTest> Get-Help hashtable 

 

Name                                       Category           Synopsis

Get-WinEvent                             Cmdlet              Gets events from event logs and event tracing log files on local and rem...

New-WSManSessionOption         Cmdlet              Creates a WS-Management session option hash table to use as input parame...

ConvertFrom-StringData             Cmdlet              Converts a string containing one or more key/value pairs to a hash table.

Group-Object                            Cmdlet              Groups objects that contain the same value for specified properties.

about_Arithmetic_Operators        HelpFile             Describes the operators that perform arithmetic in Windows PowerShell.

about_hash_tables                      HelpFile             Describes how to create, use, and sort hash tables in Windows PowerShell.

 

PS C:\hsgTest> Get-Help "hash table"

 

Name                                       Category           Synopsis

Get-WinEvent                             Cmdlet              Gets events from event logs and event tracing log files on local and rem...

New-WSManSessionOption         Cmdlet              Creates a WS-Management session option hash table to use as input parame...

New-ModuleManifest                 Cmdlet              Creates a new module manifest.

Format-Table                             Cmdlet              Formats the output as a table.

ConvertFrom-StringData             Cmdlet              Converts a string containing one or more key/value pairs to a hash table.

Get-Date                                   Cmdlet              Gets the current date and time.

Import-LocalizedData                 Cmdlet              Imports language-specific data into scripts and functions based on the U...

Select-Object                             Cmdlet              Selects specified properties of an object or set of objects. It can also...

Group-Object                            Cmdlet              Groups objects that contain the same value for specified properties.

Sort-Object                               Cmdlet              Sorts objects by property values.

Select-XML                                Cmdlet              Finds text in an XML string or document.

Set-WmiInstance                        Cmdlet              Creates or updates an instance of an existing Windows Management Instrum...

about_Arithmetic_Operators        HelpFile             Describes the operators that perform arithmetic in Windows PowerShell.

about_Assignment_Operators      HelpFile             Describes how to use operators to assign values to variables.

about_Automatic_Variables         HelpFile             Describes variables that store state information for Windows PowerShell.

about_data_sections                   HelpFile             Explains Data sections, which isolate text strings and other read-only

about_hash_tables                      HelpFile             Describes how to create, use, and sort hash tables in Windows PowerShell.

about_preference_variables         HelpFile             Variables that customize the behavior of Windows PowerShell

about_remote_output                 HelpFile             Describes how to interpret and format the output of remote commands.

about_script_internationalization HelpFile Describes the script internationalization features of Windows PowerShell...

about_Variables                         HelpFile             Describes how variables store values that can be used in Windows

about_Windows_PowerShell_2.0   HelpFile             Describes the new features that are included in Windows PowerShell 2.0.

One reason for the three different permutations of hash table is that the term hashtable appears in a few parameter names. The other articles always refer to hash table as two words. The one instance of hash-table in the Get-Winevent article is a bug.

JB, first I would like to play around with the Group-Object cmdlet. It is cool. The Group-Object cmdlet has an ashashtable switched parameter that causes the cmdlet to return a hash table. I like to use this parameter, and store the resulting hash table in a variable. In the following command, I use dir (an alias for the Get-ChildItem cmdlet) to produce a listing of the hsgtest directory. The recurse switch tells the cmdlet to continue retrieving information from nested directories. I pass the returned objects via the pipeline to the select command (select is an alias for the Select-Object cmdlet). I then pipe the custom object (the object only contains two properties: name and fullname) to the Group-Object cmdlet where the results are grouped on the name property and returned as a hash table. The hash table is stored in the $a variable. The command to obtain a directory listing, choose the name and fullname properties, group on the name property, and return a hash table is shown here:

PS C:\hsgTest> $a = dir -Recurse | select name, fullname | Group-Object -Property name –AsHashTable

After I have a hash table stored in the $a variable, I can look at the keys or the values. First I use the keys property to examine the keys stored in the $a variable. The command and associated output appear here:

PS C:\hsgTest> $a.Keys

 

testfile30.txt

testfile27.txt

testfile21.txt

testfile20.txt

testfile25.txt

testfile10.txt

<output is truncated to save space>

Now, I want to see the values that are stored in the $a variable. This is where things begin to get interesting. The command and associated output appear here in truncated fashion.

PS C:\hsgTest> $a.Values

Name                                                        FullName

----                                                              --------

testfile30.txt                                              C:\hsgTest\testfile30.txt

testfile30.txt                                              C:\hsgTest\hsgtest2\testfile30.txt

testfile30.txt                                              C:\hsgTest\hsgtest2\hsgTest3\testfile30.txt

testfile27.txt                                              C:\hsgTest\hsgtest2\testfile27.txt

testfile21.txt                                              C:\hsgTest\hsgtest2\testfile21.txt

testfile20.txt                                              C:\hsgTest\testfile20.txt

testfile20.txt                                              C:\hsgTest\hsgtest2\testfile20.txt

<output is truncated to save space>

The output above is a bit unexpected because it looks like once again the key and the values are appearing.

In addition, it looks like there are duplicate values for the key. The output of the hash table itself, however, includes grouping information that makes it clear that duplicates are not stored in the $a variable.

PS C:\hsgTest> $a

 

Name                           Value

----                           -----

testfile30.txt                 {@{Name=testfile30.txt; FullName=C:\hsgTest\testfile30.txt}, @{Name=testfile30.txt; F...

testfile27.txt                 {@{Name=testfile27.txt; FullName=C:\hsgTest\hsgtest2\testfile27.txt}}

testfile21.txt                 {@{Name=testfile21.txt; FullName=C:\hsgTest\hsgtest2\testfile21.txt}}

testfile20.txt                 {@{Name=testfile20.txt; FullName=C:\hsgTest\testfile20.txt}, @{Name=testfile20.txt; F...

<output truncated to  save space>

A closer look reveals that we have a more complex object stored in the values property. To take this closer look I can use the item method to retrieve a specific entry from the hash table. The following command shows the information stored in the value portion of the hash table.I can use the item method to retrieve a specific entry from the hash table. The following command shows the information stored in the value portion of the hash table.

PS C:\hsgTest> $a.Item("testfile30.txt")

 

Name                                                        FullName

----                                                              --------

testfile30.txt                                              C:\hsgTest\testfile30.txt

testfile30.txt                                              C:\hsgTest\hsgtest2\testfile30.txt

testfile30.txt                                              C:\hsgTest\hsgtest2\hsgTest3\testfile30.txt

When I use the item method to retrieve information about the testfile30.txt key, three items are stored in the value. To see only one of those items, I can index into it by using a square bracket and index number (you may recall this is one of my top ten favorite Windows PowerShell tricks). The code to do this is shown here along with the associated output:

PS C:\hsgTest> $a.Item("testfile30.txt")[0] 

 

Name                                                        FullName

----                                                              --------

testfile30.txt                                              C:\hsgTest\testfile30.txt

When I address a single item, I can directly access the properties I chose via the Select-Object cmdlet. In the following code, I access the name property, and then I access the fullname property:

PS C:\hsgTest> $a.Item("testfile30.txt")[0].name

testfile30.txt

PS C:\hsgTest> $a.Item("testfile30.txt")[0].fullname

C:\hsgTest\testfile30.txt

Because I can store lots of different types of things in a hash table (even other hash tables), I do not need to create a custom object via the Select-Object cmdlet. I can, in fact, store an entire fileinfo or directoryinfo object in the hash table. This is illustrated here:

PS C:\hsgTest> $b = dir -Recurse | Group-Object -Property name -AsHashTable

PS C:\hsgTest> $b.Item("testfile30.txt") 

 

    Directory: C:\hsgTest 

 

Mode               LastWriteTime                Length Name

-a---                 7/1/2011   8:53 PM        0 testfile30.txt 

 

    Directory: C:\hsgTest\hsgtest2 

 

Mode               LastWriteTime                Length Name

-a---                 7/1/2011   8:53 PM        0 testfile30.txt 

 

    Directory: C:\hsgTest\hsgtest2\hsgTest3 

 

Mode               LastWriteTime                Length Name

-a---                 7/1/2011   8:53 PM        0 testfile30.txt 

 

PS C:\hsgTest> $b.Item("testfile30.txt")[0] 

 

    Directory: C:\hsgTest 

 

Mode               LastWriteTime                Length Name

-a---                 7/1/2011   8:53 PM        0 testfile30.txt

Any of the properties associated with a fileinfo object are now accessible. In the following code, I retrieve the lastwritetime property from the file:

PS C:\hsgTest> $b.Item("testfile30.txt")[0].LastWriteTime 

Friday, July 01, 2011 8:53:46 PM

The Get-WinEvent cmdlet can use a hash table for a filter. For an excellent article on that technique, see Use PowerShell Cmdlet to Filter Event Log for Easy Parsing.

The Format-Table cmdlet accepts a hash table to create customized output. The hash table takes the place of a single property, and consists of two key/value pairs. The first key is label and the second key is expression. The expression key accepts a script block. The semicolon separates the two key/value pairs. The ampersand, opening brace, and closing brace (@{}) delineate a hash table. The script block value for the expression key also uses a pair of braces to delineate the script block.

In the following code, dir (an alias for the Get-ChildItem cmdlet) returns a directory listing. The resulting directoryinfo and fileinfo objects are piped to the Where-Object cmdlet (the ? is an alias for the Where-Object cmdlet). The mode property is inspected and only objects that do not have a mode that begins with “d” (a mode of “d----“ is a directory) are passed to the Format-Table cmdlet (ft is an alias for the Format-Table cmdlet). The name property from the fileinfo object is selected, and a custom label called last written is added to the table. The last written property will display the lastwritetime property from the fileinfo object. The code is shown here:

dir | ? { $_.mode -notmatch '^d' } | ft name, @{Label="last written" ; Expression = {$_.LastWriteTime}}

The command and associated output are shown in the following figure.

Image of command and associated output

 

JB, that is all there is to using hash tables inside Windows PowerShell commands. This also ends Hash Table Week. Join me tomorrow when I will talk about playing around with the day of the year. Sound boring? It is not—trust me. See you tomorrow.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

 

 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Ed, a bit confused here. In the 1st blog post, you mentioned that the key has to be unique, but here, we have a key that corresponds to multiple fullnames.

    By the way, these blog posts have been great, thanks a lot.

  • Very good article, Ed!

    I supose, you could even dive deeper and deeper into the fantastic world of hashtables! There is plenty of stuff left to experiment with for us now! Nesting objects and retrieveing particular properties or methods of them is really sometimes a bit of an art and surely needs some experience and maybe try-and-error ...

    One little thing, I stumbled upon reading your article:

    You shortened the comandline listing a little bit too much :-(

    look at:

    "PS C:\hsgTest> $a.Keys"

    ...

    After: "<output is truncated to save space>" you truncated the next line, too :-)

    A command that produces the next, shortened output is missing.

    It might be a line like that:

    "C:\hsgTest> $a.Values" ???

    Klaus

  • @Srikanth: You are still thinking the right thing :-)

    The data structure returned from "group-object" is a little bit more complicated!

    It is nested and the resulting Hashtable uses the filename as unique key, right!!

    But the values of this hashtable are collections again:

    PS C:\Users\Schulte> $a["testfile10"].gettype() | ft -auto

    IsPublic IsSerial Name         BaseType    

    -------- -------- ----         --------    

    True     True     Collection`1 System.Object

    Each single value is of type Collection`1 and has a repeated property value for "Name" that corresponds to the key of the Hashtable returned by "group-object"

    PS C:\Users\Schulte> $a["testfile10"] | ft -auto

    Name       FullName                            

    ----       --------                            

    testfile10 D:\Temp\testfiles\testfile10        

    testfile10 D:\Temp\testfiles\folder1\testfile10

    testfile10 D:\Temp\testfiles\folder2\testfile10

    testfile10 D:\Temp\testfiles\folder3\testfile10

    So now you have four members of the collection with identical names and distinct Fullname properties! As members of a collection don't need to be unique, this may solve the confusion .... ( or I made it even bigger now :-))

    Klaus

  • Thanks for the great article as always Ed,

    I do agree with Srikanth and Klaus's comments tough.  

    I was confused by the three matching keys as I thought they had to be unique.  When I tried to write an existing key even though the value was different I received.

    "Exception calling "Add" with "2" argument(s): "Item has already been added. Key in dictionary:"

    Also I noticed what Klaus mentioned where the command to produce the second set of output was omitted.  However Klaus the command was simply $a not $a.values as this would have merely displayed the values and not the pairs.

    Thanks,

    Ryan  

  • After running through a few examples on my own, I now see what is happening. The key is indeed unique, the value is a collection.

    Thanks, Klaus, for pointing out the same as well :-)

  • @Srikanth I am sorry you got a bit confused. The key has to be unique. The values associated with the key are not unique. The thing that is a bit confusing is that the values associated with the keys are in fact collections (as @Klaus points out).

    I am glad you are enjoying the articles.

    @Klaus Schulte You are correct, I did truncate the command as well as the output (sorry about that). I have edited the post to make the section a bit clearer, and to include the missing command. To make it easy to spot, the missing command was in fact $a.values

    @Ryan Shafer you are also right it is a bit confusing. In addition, you are right about the missing command ...

    NOW, how can both @Klaus and @Ryan be right? I changed the article a bit to include both commands.

  • hullo Ed, i am trying to wrap my head around the concept of HashTables and their use. to me, a VERY novice PowerShell guy and very little scripting experience, it LOOKS like an array.  but an array you have to populate yourself to use for something else.  now i've seen where you've piped get-Process or services or whatever into the HashTable and that seems like it is a more functional use of it. but once again, it just looks like a multi-dimensional array. to me, it sounds like it could be a good idea and i know people use them but HashTables still seem confusing even after your 4-parter, which i thought was enlightening.