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 ampersand 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.
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 the best 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.
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