Search code examples
powershellparameter-splatting

Powershell splatting a nested hash table


I have a function that returns a complex nested hash table data structure, where part of it forms the arguments for a further function call, and part of it is strings for reporting errors that result in the arguments not being populated. Ideally I would like to splat just the arguments hash table, but I am starting to think that can't be done. So, an example of the basic problem looks like this...

function Test {
    param (
        [String]$A,
        [String]$B,
        [String]$C
    )
    Write-Host "A: $A"
    Write-Host "B: $B"
    Write-Host "C: $C"
}

$data = @{
    arguments = @{
        A = 'A string'
        B = 'Another string'
        C = 'The last string'
    }
    kruft = 'A string that doesn not need to get passed'
}

Ideally I want to splat $data.arguments, so something like this... Test @data.arguments But that doesn't work, resulting in the error

The splatting operator '@' cannot be used to reference variables in an expression. '@data' can be used only as an argument to a command. To 
reference variables in an expression use '$data'.

So I tried... Test @(data.arguments) Which results in the error

data.arguments : The term 'data.arguments' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the 
spelling of the name, or if a path was included, verify that the path is correct and try again.

I also tried... Test @($data.arguments) Which results in the whole hash table being passed as a single argument and the output is

A: System.Collections.Hashtable
B: 
C:

What DOES work is...

$arguments = $data.arguments
Test @arguments

Which has me thinking you really cannot splat anything but a simple variable that is an appropriate hash table. But, I am hoping someone can verify that is indeed true, or point out the solution I haven't come up with yet. The actual code requires 5 arguments, with somewhat verbose names because I prefer descriptive names, so splatting is very much an appropriate solution. Needing to make a new variable with just the hash table to be passed isn't an issue, just wondering if it really is the only option.


Solution

  • That's not possible.

    Any variable (and only variables) provided with "@" instead of "$" for a function parameter is declared as TokenKind "SplattedVariable".

    See: https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.language.tokenkind?view=powershellsdk-7.0.0

    PS: Some quick tests from my side which could have succeed (apart from PS design):

    Write-Host 'Test 1' -ForegroundColor Yellow
    Test @$data.arguments
    
    Write-Host 'Test 2' -ForegroundColor Yellow
    Test @$($bla = $data.arguments)
    
    Write-Host 'Test 3' -ForegroundColor Yellow
    Test @$bla = $data.arguments
    
    Write-Host 'Test 4' -ForegroundColor Yellow
    Test @$bla = $data.arguments.GetEnumerator()
    
    Write-Host 'Test 5' -ForegroundColor Yellow
    Test @$($data.arguments.GetEnumerator())
    

    ... but they didn't.