Tag Archives: hashtable

Rename Hashtable Key Revised


Last week I posted an advanced PowerShell function to rename a hashtable key. As usual, the more I worked with it the more I realized it was missing something – namely the ability the take a pipelined object. My original version assumed you had saved the hashtable to a variable. But as I was working with ConvertTo-Hashtable I realized the shortcoming. The solution was to modify Rename-Hashtable so that it could accept a hashtable as a piped value.

I won’t go through the function again. You can read the original post to learn more about how it works. Let’s look at what changed. Because I wanted to retain the option to also specify a variable name, I created two parameters sets. One for the piped object and one for the variable name.

[cmdletbinding(SupportsShouldProcess=$True,DefaultParameterSetName="Pipeline")]

Param(
[parameter(Position=0,Mandatory=$True,
HelpMessage="Enter the name of your hash table variable without the `$",
ParameterSetName="Name")]
[ValidateNotNullorEmpty()]
[string]$Name,
[parameter(Position=0,Mandatory=$True,
ValueFromPipeline=$True,ParameterSetName="Pipeline")]
[ValidateNotNullorEmpty()]
[object]$InputObject,
[parameter(position=1,Mandatory=$True,
HelpMessage="Enter the existing key name you want to rename")]
[ValidateNotNullorEmpty()]
[string]$Key,
[parameter(position=2,Mandatory=$True,
HelpMessage="Enter the NEW key name")]
[ValidateNotNullorEmpty()]
[string]$NewKey,
[switch]$Passthru,
[ValidateSet("Global","Local","Script","Private",0,1,2,3)]
[ValidateNotNullOrEmpty()]
[string]$Scope="Global"
)

I defined parameter sets called Pipeline and Name and made the former the default in the cmdletbinding attribute. Because the remaining parameters would be in both parameter sets I didn’t specify one. When looking at the function’s help you can see the result.

rename-hashtable-paramsets

Because I’m taking input from the pipeline, I needed to add a Process scriptblock. Within the scriptblock, if an object has been piped in, I turn on the passthru variable and create a temporary copy of the piped in hashtable.

Process {
    #validate Key and NewKey are not the same
    if ($key -eq $NewKey) {
     Write-Warning "The values you specified for -Key and -NewKey appear to be the same. Names are NOT case-sensitive"
     Return
    }

    Try {
        #validate variable is a hash table
        if ($InputObject) {
            $name="tmpInputHash"
            Set-Variable -Name $name -Scope $scope -value $InputObject
            $Passthru=$True
        }

The rest of the code worked just fine and there was no reason to change it. All I needed to do was transform the -Inputobject value into the -Name value since I already had code that used $Name. Sometimes you need separate code blocks but in this case I didn’t. Once the transformation is complete, the rest of the function runs as originally designed. With this version I can now run commands like this:

PS C:\> $h = get-service spooler -computer Serenity | convertto-hashtable -NoEmpty -Exclude CanStop,CanPauseAndcontinue | rename-hashtable -key machinename -new computername
PS C:\> $h

Name                           Value
—-                           —–
computername                   Serenity
Name                           spooler
ServiceName                    spooler
RequiredServices               {RPCSS, http}
DependentServices              {Fax}
ServiceType                    Win32OwnProcess, InteractiveProcess
Status                         Running
ServicesDependedOn             {RPCSS, http}
ServiceHandle                  SafeServiceHandle
DisplayName                    Print Spooler

Download Rename-Hashtable2 and give it a go.

Post to Twitter Post to Plurk Post to Yahoo Buzz Post to Delicious Post to Digg Post to Facebook Post to FriendFeed Post to Google Buzz Post to Ping.fm Post to Reddit Post to Slashdot Post to StumbleUpon Post to Technorati

Convert PowerShell Object to Hashtable Revised


squarepatternA while back I posted an advanced PowerShell function that would take an object and convert it to a hashtable. The premise was simple enough: look at the incoming object with Get-Member to discover the property names then create a hashtable with each property name as a hashtable key. I’ve a need to use this over the last few weeks and realized the function could use a little updating.

The new version works like the old, with the addition of one new parameter. I decided I wanted a way to exclude properties from the hashtable. Of course this presumes you know in advance about the object you are working with and what properties you wish to exclude. This is the core segment of code, which has also been revised over version 1.

#go through the list of names and add each property and value to the hash table
$names | ForEach-Object {
    #only add properties that haven’t been excluded
    if ($Exclude -notcontains $_) {
        #only add if -NoEmpty is not called and property has a value
        if ($NoEmpty -AND -Not ($inputobject.$_)) {
           Write-Verbose "Skipping $_ as empty"
        }
        else {
           Write-Verbose "Adding property $_"
           $hash.Add($_,$inputobject.$_)
    }
    } #if exclude notcontains
    else {
       Write-Verbose "Excluding $_"
    }
} #foreach

In the first version I was duplicating some code and I generally try to avoid that. In this snippet $names is the collection of property names and $Inputobject is the piped in object. Armed with this tool I can create hashtables from objects with just the properties I want.

PS Scripts:\> $h = get-service spooler | ConvertTo-HashTable -NoEmpty -Exclude CanStop,CanPauseandContinue
PS Scripts:\> $h

Name                           Value
—-                           —–
ServiceName                    spooler
ServiceType                    Win32OwnProcess, InteractiveProcess
Name                           spooler
DisplayName                    Print Spooler
MachineName                    .
Status                         Running
ServiceHandle                  SafeServiceHandle
DependentServices              {Fax}
RequiredServices               {http, RPCSS}
ServicesDependedOn             {http, RPCSS}

This version should work in PowerShell 2.0 or 3.0. Download ConvertTo-Hashtable2.

Post to Twitter Post to Plurk Post to Yahoo Buzz Post to Delicious Post to Digg Post to Facebook Post to FriendFeed Post to Google Buzz Post to Ping.fm Post to Reddit Post to Slashdot Post to StumbleUpon Post to Technorati

Rename Hashtable Key


talkbubbleI use hashtables quite a bit. Often generating hashtables on the fly from other sources. But sometimes the hashtable keys that come from these external sources don’t align with what I intend to do with the hashtable. For example, one of the nifty things you can do with hashtables is splat them against a cmdlet or advanced function. Each hashtable key will bind to its corresponding parameter name. This makes it very easy to run a command and pass a bunch of parameters all at once. Here’s an example of splatting in action.

PS C:\> $hash = @{Computername="Serenity";Class="AntivirusProduct";Namespace="root\securitycenter2"}
PS C:\> get-wmiobject @haSH

__GENUS                  : 2
__CLASS                  : AntiVirusProduct
__SUPERCLASS             :
__DYNASTY                : AntiVirusProduct
__RELPATH                : AntiVirusProduct.instanceGuid="{D68DDC3A-831F-4fae-9E44-DA132C1ACF46}"
__PROPERTY_COUNT         : 6
__DERIVATION             : {}
__SERVER                 : SERENITY
__NAMESPACE              : ROOT\securitycenter2
__PATH                   : \\SERENITY\ROOT\securitycenter2:AntiVirusProduct.instanceGuid="{D68DDC3A
                           -831F-4fae-9E44-DA132C1ACF46}"
displayName              : Windows Defender
instanceGuid             : {D68DDC3A-831F-4fae-9E44-DA132C1ACF46}
pathToSignedProductExe   : %ProgramFiles%\Windows Defender\MSASCui.exe
pathToSignedReportingExe : %ProgramFiles%\Windows Defender\MsMpeng.exe
productState             : 397568
timestamp                : Fri, 04 Jan 2013 17:09:42 GMT

This works because all of the hashtable keys line up with parameter names. If the names don’t match they are ignore. So sometimes I need to rename the hashtable key. This isn’t too difficult conceptually.

1. Create a new entry with the new key name and the old value
2. Remove the existing key.

But, I decided to turn this into an advanced function, probably with more bells and whistles than I really need. But maybe you will learn something new from my efforts.

Function Rename-HashTable {
#comment based help is here

[cmdletbinding(SupportsShouldProcess=$True)]

Param(
[parameter(position=0,Mandatory=$True,
HelpMessage="Enter the name of your hash table variable without the `$")]
[ValidateNotNullorEmpty()]
[string]$Name,
[parameter(position=1,Mandatory=$True,
HelpMessage="Enter the existing key name you want to rename")]
[ValidateNotNullorEmpty()]
[string]$Key,
[parameter(position=2,Mandatory=$True,
HelpMessage="Enter the NEW key name")]
[ValidateNotNullorEmpty()]
[string]$NewKey,
[switch]$Passthru,
[ValidateSet("Global","Local","Script","Private",0,1,2,3)]
[ValidateNotNullOrEmpty()]
[string]$Scope="Global"
)

#validate Key and NewKey are not the same
if ($key -eq $NewKey) {
 Write-Warning "The values you specified for -Key and -NewKey appear to be the same. Names are NOT case-sensitive"
 Return
}

Try {
    #validate variable is a hash table
    write-verbose (gv -Scope $scope | out-string)
    Write-Verbose "Validating $name as a hashtable in $Scope scope."
    #get the variable
    $var = Get-Variable -Name $name -Scope $Scope -ErrorAction Stop
       
    if ( $var.Value -is [hashtable]) {
        #create a temporary copy
        Write-Verbose "Cloning a temporary hashtable"
        <#
            Use the clone method to create a separate copy.
            If you just assign the value to $temphash, the
            two hash tables are linked in memory so changes
            to $tempHash are also applied to the original
            object.
        #>

        $tempHash = $var.Value.Clone()
        #validate key exists
        Write-Verbose "Validating key $key"
        if ($tempHash.Contains($key)) {
            #create a key with the new name using the value from the old key
            Write-Verbose "Adding new key $newKey to the temporary hashtable"
            $tempHash.Add($NewKey,$tempHash.$Key)
            #remove the old key
            Write-Verbose "Removing $key"
            $tempHash.Remove($Key)
            #write the new value to the variable
            Write-Verbose "Writing the new hashtable"
            Write-Verbose ($tempHash | out-string)
            Set-Variable -Name $Name -Value $tempHash -Scope $Scope -Force -PassThru:$Passthru |
            Select-Object -ExpandProperty Value
        }
        else {
            Write-Warning "Can’t find a key called $Key in `$$Name"
        }
    }
    else {
        Write-Warning "The variable $name does not appear to be a hash table."
    }
} #Try

Catch {
    Write-Warning "Failed to find a variable with a name of $Name."
}

Write-Verbose "Rename complete"
} #end Rename-Hashtable

The function assumes you have saved the hashtable to a variable. Pass the variable name as a parameter, without the $ character. The function uses Get-Variable to retrieve the variable. Remember that variables are scope specific so I need to specify the scope to search. I’m defaulting to the global scope. Otherwise, when the function runs, it is in its own scope which doesn’t include the hashtable variable. Now, I could have simply attempted to read the variable, trusting PowerShell to read up the scope until it found it, but the best practice is to avoid referencing out of scope items.

Once the variable is found, its value is the actual hashtable which I verify as a [hashtable] object.

if ( $var.Value -is [hashtable]) {

From here I create a cloned copy of the original variable.

$tempHash = $var.Value.Clone()

By the way, you’ll notice my functions supports ShouldProcess. This means I can use -WhatIf and it will be passed to any cmdlets that also support it. At the end of my command I use Set-Variable which supports ShouldProcess so if I run my function with -WhatIf, Set-Variable will also use -Whatif. This is a long way of saying I can run the command in “test” mode without having to worry about changing the original hash table. This is also why I create a clone instead of simply setting the value of my temporary hash table to the original hash table. When you do that, the two variables are actually linked so any changes in one are made to other. Here’s an example of what I’m talking about:

PS C:\> $a = @{A=1;B=2;C=3}
PS C:\> $a

Name                           Value
—-                           —–
A                              1
B                              2
C                              3

PS C:\> $b=$a
PS C:\> $b

Name                           Value
—-                           —–
A                              1
B                              2
C                              3

PS C:\> $b.Add("D",4)
PS C:\> $b

Name                           Value
—-                           —–
A                              1
B                              2
D                              4
C                              3

PS C:\> $a

Name                           Value
—-                           —–
A                              1
B                              2
D                              4
C                              3

Since I want my temp copy to be “unattached”, I use the hashtable’s Clone() method. From here, it is simply a matter of creating the new key with the old value, and removing the old key with a little error handling along the way.

if ($tempHash.Contains($key)) {
    #create a key with the new name using the value from the old key
    Write-Verbose "Adding new key $newKey to the temporary hashtable"
    $tempHash.Add($NewKey,$tempHash.$Key)
    #remove the old key
    Write-Verbose "Removing $key"
    $tempHash.Remove($Key)
    #write the new value to the variable
    Write-Verbose "Writing the new hashtable"
    Write-Verbose ($tempHash | out-string)
    Set-Variable -Name $Name -Value $tempHash -Scope $Scope -Force -PassThru:$Passthru |
    Select-Object -ExpandProperty Value
}

At the end of the process I use Set-Variable to update my original hashtable variable. By default the cmdlet doesn’t write anything to the pipeline but on the chance I might need the output, I use -Passthru and send the variable value, which is the revised hashtable, back to the pipeline. With this function I can now do something like this very easily.

PS C:\> $hash = @{name="Serenity";Class="AntivirusProduct";Namespace="root\securitycenter2"}
PS C:\> $hash

Name                           Value
—-                           —–
name                           Serenity
Class                          AntivirusProduct
Namespace                      root\securitycenter2

PS C:\> Rename-HashTable -Name hash -Key name -NewKey Computername
PS C:\> $hash

Name                           Value
—-                           —–
Class                          AntivirusProduct
Namespace                      root\securitycenter2
Computername                   Serenity

Now $hash has the right key names for splatting, or whatever else I have in mind. This function can come in handy with ConvertTo-HashTable. Download Rename-Hashtable and as always I hope you’ll let me know what you think.

Post to Twitter Post to Plurk Post to Yahoo Buzz Post to Delicious Post to Digg Post to Facebook Post to FriendFeed Post to Google Buzz Post to Ping.fm Post to Reddit Post to Slashdot Post to StumbleUpon Post to Technorati