How to Create Hash Table from String Data?

Hoping to get some help converting strings into objects. I have a bunch of output that looks like the following:

Sample

LOGICAL UNIT NUMBER 68
Name TIER2_LUN068
Current owner: SP B

LOGICAL UNIT NUMBER 110
Name TIER1_LUN110
Current owner: SP A

LOGICAL UNIT NUMBER 203
Name TIER1_LUN203
Current owner: SP B

LOGICAL UNIT NUMBER 81
Name TIER1_LUN081
Current owner: SP A

End Sample

I want to parse through the string output and bring each of the (3) properties into a hash table.

The pseudo code of what I’m trying to accomplish is as follows:
Gather the output into a variable ($getLuns)
Loop through the output line by line, ($Line) and breaking it apart by blank spaces ($Parts)
Search each part for specific value
When found add the item to hash table (Name, value)

My function doesn’t appear to be working since the key value name is repeating. It gives the error that “Item has already been added”

function Get-EMCLUNInfo { 
    [CmdletBinding()]

    Param([Parameter(Mandatory=$true)]
              [string] $TAIP)

    Begin {
        $LHash = $null
        $LHash = @{}
        $GetLUNs = & "$NaviPath\NaviSECCli.exe" -h $TAIP getlun -name -owner
    }

     Process {
	    foreach ($line in $GetLUNs){ 
            $parts = [regex]::Split($line, '\s+') 
		    if($parts[0] -eq 'LOGICAL') {
		        $LUN = [int]$parts[3]
                        $LHash.Add('LUN_ID',$LUN)
		    } 
		    if($parts[0] -eq 'Name') { 
		        $LName = [string]::Join(' ', $parts[1..$($parts.Count)]) 
                        $LHash.Add('LUN_Name',$LName)		        
		    }
                    if($parts[0] -eq 'Current') { 
		        $SPID = $parts[3]
		        $LHash.Add('SP',$SPID)
	             }  
             }
    }

    End {$LHash}
}

Get-EMCLUNInfo '192.168.8.97'

Again I’m still new, so if there’s a better way to accomplish (Maybe using a switch statement instead of if) please let me know. Thank you in advance for any and all help.

Anybody ideas out there on how to best do this?

OK, I’ve been messing with this seem to be getting closer…but still not fully there.

Function Get-EMCLUNInfo {
    [CmdletBinding()]

    Param([Parameter(Mandatory=$true)]
              [string] $TAIP)

    Begin {
        $LHash = $null
        $LHash = @{}
        $GetLUNs = & "$NaviPath\NaviSECCli.exe" -h $TAIP getlun -name -owner
    }
    Process {
        $GetLUNs | ForEach-Object {
            switch -Regex ($_) {
                "LOGICAL*" { $obj = New-Object -TypeName psobject; $obj | Add-Member NoteProperty "LUN_ID" -Value ($_.split(" ")[3].trim()); break } 
	        "Name*"    { $obj | Add-Member NoteProperty "LUN_Name" -Value  ($_.split(" ")[1].trim()); break } 
		"Current*" { $obj | Add-Member NoteProperty "LUN_SP" -Value ($_.split(" ")[1..($_.Count)]); $LHash += $obj } 
	     }  
        }     
    } 
    End {}
}


Get-EMCLUNInfo '192.168.8.97'

Running this gives the error “A hash table can only be added to another hash table.” Any help is greatly appreciated.

So, let’s break out just creating a custom object. Although it works, using Add-Member IMHO is the most inefficient way to build an object.

Function Get-It {
    begin{
    #Generate a blank array to place objects into
    $LHash = @()
    }
    process{
        
        for($i=0;$i -le 5;$i++) {
            #The main issue is understanding that each "row" is an object,
            #so you are generating an object for each item processed
            $object = New-Object -TypeName psobject
            #Now there is a blank object with no properties, so
            #to add properties you are using Add-Member.
            $object|Add-Member NoteProperty 'Number' -Value $i
            #Now you have to append the row to the blank array
            $LHash += $object
        }
    }
    end{
        #Return your variable
        $LHash
    }
}

In the above example you are doing A LOT of work with mulitple variables to generate a single object. Here is the best way I’ve found (Thank you Dave) to create a object:

Function Get-It {
    begin{
        #Generate a blank array to place objects into
        $object = @()
    }
    process{
        #Take all results from the for loop and add them to the blank array
        $object = for($i=0;$i -le 5;$i++) {
            #Create variables for all your individual results (e.g. your switch regex)
            $myValue = "AValue{0}" -f $i
            #Generate a new object and assign the appropriate variable to the property
            New-Object -TypeName PSObject -Property @{Number=($myValue)}
        }
    }
    end{
        #Return your variable
        $object
    }
}

Good uses for Add-Member would be adding a static (same value) to all rows at once like:

PS C:> $test = Get-It

PS C:> $test | Add-Member -MemberType NoteProperty -Name AnotherColumn -Value “AStaticValue”

or adding Aliases to objects properties.

This type of list output looks fairly straightforward to parse. In your sample text, each new object begins with a LOGICAL UNIT NUMBER line, so we can use that line as the indicator to finish creating the previous object (if any), and start building a new one. The code might look something like this:

function Get-EMCLUNInfo { 
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [string] $TAIP
    )
 
    $LHash = @{}
    $GetLUNs = & "$NaviPath\NaviSECCli.exe" -h $TAIP getlun -name -owner

    foreach ($line in $GetLUNs){ 
        switch -Regex ($line) {
            '^LOGICAL UNIT NUMBER (\d+)' {
                if ($LHash.Count -gt 0) {
                    New-Object psobject -Property $LHash
                }

                $LHash = @{ LUN_ID = [int]$matches[1] }
                break
            }

            '^Name\s*(.*)$' {
                $LHash['LUN_Name'] = $matches[1]
                break
            }

            '^Current owner:\s*SP\s*(.*)$' {
                $LHash['SP'] = $matches[1]
                break
            }
        }
    }

    if ($LHash.Count -gt 0) {
        New-Object psobject -Property $LHash
    }
}

That worked! Thank you both for the input.

Quick Question; What does the conditional statement at the end do?

if ($LHash.Count -gt 0) {
    New-Object psobject -Property $LHash

Also, I noticed that I can’t sort based on LUN_ID, any idea how to fix that. When looking at Get-Member, it looks like the noteproperty’s are not expandable (Not sure if that matters)

TypeName: System.Management.Automation.PSCustomObject

Name                 MemberType   Definition                         
----                       ----------              ----------                         
Equals               Method              bool Equals(System.Object obj)     
GetHashCode  Method              int GetHashCode()                  
GetType              Method             type GetType()                     
ToString             Method              string ToString()                  
LUN_ID              NoteProperty    System.Int32 LUN_ID=1              
LUN_Name       NoteProperty    System.String LUN_Name=TIER0_LUN001
LUN_SP             NoteProperty    System.String LUN_SP=A