Home > database >  PowerShell Hashtable - how to select property
PowerShell Hashtable - how to select property

Time:11-10

I need to get the value of an environment variable from a kubernetes pod. I have my values listed in a hash table.

I call $hash["service-testurl"].spec.template.spec.containers.env And it returns a table:

name                                            value
----                                            -----
ADDR                                      https://test.com
TOKEN                                     123456789
CERT_PATH                                 public-certs/test
ENVIRONMENT                               dev

I need to get https://test.com into a variable in my ps1 script, but i'm not sure how to get this value. (consider that for each deployment the url will be different, like abc.com, def.com, ghj.com... so i can't filter by the name test.com)

I was looking for something like $hash["service-testurl"].spec.template.spec.containers.env.name["ADDR"].value

Running $hash["service-testurl"].spec.template.spec.containers.env.PSTypeNames returns

System.Object[]
System.Array
System.Object

CodePudding user response:

I was able to do it with something similar that @iRon proposed:

$hash["service-testurl"].spec.template.spec.containers.env.where{$_.name -eq 'ADDR'}.value

Thanks!

CodePudding user response:

To complement your own effective solution:

Even though your display output of $hash["service-testurl"].spec.template.spec.containers.env looks like the representation of a (single) hashtable, the value is actually:

  • (a) an array, as your diagnostic output with .pstypenames demonstrates,

  • (b) whose elements are (presumably) [pscustomobject] instances that each have a a .name and a .value property (an easy way to tell is that the display output's column headers are name and value, whereas with hashtables they would be Name and Value).

Leaving aside that the identifier ADDR is a property value rather than a property / key name in your case, you fundamentally cannot use key-based index notation (['ADDR']) on an array - that generally only works on a (single) hashtable (or, more generally, dictionary).[1]

In your case, you need to find the array element whose .name property value is 'ADDR', which then allows you to return its .value property value.

For collections already in memory, the intrinsic .Where() method (as used in your own solution) is a more efficient - and more flexible - alternative to filtering a collection via the Where-Object cmdlet.

It will often not matter in practice, but you can optimize a .Where() call to stop filtering once the first match has been found, if you expect or are only interested in one match:

$hash["service-testurl"].spec.template.spec.containers.env.Where(
  { $_.name -eq 'ADDR' },
  'First'
).value

Note that .Where() always returns an array-like collection, even if only a single value is matched - see this answer for details. As such, the .value property access is attempted on that collection, which, however, still works as intended, courtesy of the PowerShell feature known as member-access enumeration.

Note how using (...) around the arguments is now a syntactic necessity.

While with only a single argument - the filter script block ({ ... }) - you can get away with not using (...) - .Where{ $_.name -eq 'ADDR' } as shorthand for .Where({ $_.name -eq 'ADDR' }) - omitting the (...) is problematic for two reasons:

  • Given that the Where-Object cmdlet can also be referred to as Where (via a built-in alias), the two command forms could be confused, and given that Where-Object allows and is typically used with a space separating the command name from its script-block argument (e.g, 1..3 | Where { $_ -eq 2 }, it is tempting to also try to use a space with the .Where() method, which does not work:

    # !! BROKEN, due to space before "{"
    (1..3).Where { $_ -eq 2 }
    
  • If you add another argument later, you need to remember to use (...)


[1] The fact that key-based index notation does not work with member-access enumeration, i.e. doesn't work on an array of hashtables (only dot notation does, which PowerShell supports for hashtables too) could be considered an inconsistency; e.g. @( @{ foo=1 } ).foo works (dot notation), but @( @{ foo=1 } )['foo'] does not, due to the array wrapper.
However, this inconsistency was declared to be by design - see GitHub issue #17514.

  • Related