October 15th, 2011

Easily Create a PowerShell Hash Table

Doctor Scripto
Scripter

Summary: Learn how to automatically populate a hash table in a Windows PowerShell script.

 

Microsoft Scripting Guy Ed Wilson here. A hash table is an important data structure in Windows PowerShell. Many of the cmdlets use hash tables to format their input. For example, if I want to create a custom column header in a table, I have to use a hash table. A hash table consists of one or more key value pairs (of course, it is possible to create an empty hash table that contains no key value pairs, but let’s go with the easy description first).

The at sign and a pair of braces (curly brackets) identify a hash table. Normally a variable stores the hash table, but it is possible to create a hash table and not store it in a variable. An example of this is shown here:

@{

“key1” = “value1”

“key2” = “value2”

}

In the following figure, I first run the code and display the contents of the hash table. Next, I pipe the results to the Get-Member cmdlet.

Image of contents of hash table

Most of the time, a hash table is stored in a variable for use in other places. It is possible to create a hash table on a single line, but it is difficult to read, and if a problem occurs, it is hard to troubleshoot. The semicolon separates key value pairs and indicates a new line. In the code seen here, I create a hash table on a single line.

$hash = @{“key1” = “value1″;”key2” = “value2”}

The same hash table is easier to read when spread out on multiple lines. This technique is shown here:

$hash1 = @{

  “key1” = “value1”

  “key2” = “value2”

 }

Whether the closing brace appears on its own line or after the final key value pair is a matter of stylistic taste. I generally prefer to place it on line because it is easier to spot when troubleshooting. When working with a script editor that automatically matches brace pairs, this advantage disappears, and I then prefer to close up the code.

The real power of hash tables comes by adding key value pairs automatically from within the script. When used in this way, hash tables are essentially temporary data storage. The advantage a hash table has over an array is the key value pairs. The keys provide a way to retrieve the associated value by name; with an array, the value is accessible via the element number. A disadvantage over an array is that with a hash table, the key must be unique; an array permits multiple elements to be the same.

To create a hash table dynamically, follow these steps:

1.       Create an empty hash table.

2.       Store the empty hash table in a variable.

3.       Collect the data.

4.       Store the collected data in a variable.

5.       Use the foreach statement to walk through the collected data.

6.       Inside the loop call the add method to add the key value pairs to the hash table.

An example of this procedure is shown here:

$hash = $null

$hash = @{}

$proc = get-process | Sort-Object -Property name -Unique

 

foreach ($p in $proc)

{

 $hash.add($p.name,$p.id)

}

The first thing I do is assign the value $null to the $hash variable. I do this because when running code multiple times in the Windows PowerShell ISE, the values of global variables continue to be present. If I am not paying attention, the value stored in variables can change with each run of the script. After I have initialized the $hash variable with $null, I create an empty hash table and store it in the $hash variable. These two lines of code are shown here:

$hash = $null

$hash = @{}

Next I use the Get-Process cmdlet to collect information about each process that is running on the computer. I sort these process objects based upon the name property, and I use the unique switched parameter to return only unique instances of the process objects. The reason for doing this is I want to use the name property as the value for the keys in my hash table. The key of a hash table must be unique, and in most cases, there are several duplicate instances of processes running on a computer. For example, the following code reveals there are several processes named svchost.

PS C:\Users\edwils> gps | ? {$_.name -eq ‘svchost’}

 

Handles             NPM(K)             PM(K)               WS(K)               VM(M)              CPU(s)               Id ProcessName

    702               20                     10324               13464               56                     612                   svchost

443               15                     6328                 11808               53                     936                   svchost

165               12                     5512                 10580               48                     1036                 svchost

653               28                     28756               28172               93                     1120                 svchost

789               31                     16132               26284               119                   1152                 svchost

2625              127                   97160               88144               438                   1184                 svchost

798               37                     19064               22760               154                   1292                 svchost

339               34                     16332               19100               92                     1312                 svchost

599               29                     9136                 16152               68                     1348                 svchost

615               28                     11352               15524               59                     1388                 svchost

105               9                      2900                 6644                 35                     2052                 svchost

86                8                      2596                 5660                 47                     2332                 svchost

350               15                     7444                 9340                 47                     4256                 svchost

524               22                     8832                 11304               56                     4812                 svchost

50                4                      1520                 3336                 13                     5560                 svchost

When I use the unique switched parameter, I retrieve only one instance of each process with the same name. I do not know which instance I obtain, but for this application, it does not matter. This line of code is shown here:

$proc = get-process | Sort-Object -Property name -Unique

Now it is time to walk through the collection of process objects and add the name and path to the hash table. I use the name of the process for the key and the process ID as the value. The foreach statement is thebest command to use to walk through the collection of process objects. I use the add method from the hashtable object that is stored in the $hash variable. The add method requires both the key and the value. This portion of the code is shown here:

foreach ($p in $proc)

{

 $hash.add($p.name,$p.id)

}

When I display the contents of the $hash variable, I am presented with the following output.

Image of contents of $hash variable

 

That is all there is to dynamically creating a hash table.

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

 

Author

The "Scripting Guys" is a historical title passed from scripter to scripter. The current revision has morphed into our good friend Doctor Scripto who has been with us since the very beginning.