Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Splatting without assigning hash or array to a variable

I have a command that returns a hash table. Like this one, for example:

function Get-TestArgs() { return @{a=1;b=2;c=3} }

And I'd like to use its return value as the arguments for my other function:

function Test($a, $b, $c) {
    Write-Host 'A' $a
    Write-Host 'B' $b
    Write-Host 'C' $c
}

This is possible with PowerShell's splatting feature:

$testargs = @{a=1;b=2;c=3}
Test @testargs

The problem is I don't want to assign the hash to an intermediate variable.

Something along these lines is what I have in mind:

Test (some splat syntax)(Get-TestArgs)

Obviously, this wouldn't work, since it just creates an array containing the hash and passes that:

Test @(Get-TestArgs)

Is there a way to achieve this?

Reason I care

I am writing instructions that will be executed manually. These instructions will call out to scripts, but there is still some manual intervention to be done between calls. So I'd like to minimize the commands to reduce the risk of something going wrong, like a variable being leftover from other commands or a typo.

like image 837
jpmc26 Avatar asked Jun 28 '16 16:06

jpmc26


2 Answers

I don't think it's possible to use splatting without a variable. But there are few workarounds to achieve what you're looking for.

  1. Write a wrapper function that would take a hashtable and invoke the 'real' function. If you wanted to call the function directly, without Get-TestArgs, you would use TestInner.

    function Get-TestArgs() { return @{a=1;b=2;c=3} }
    
    function TestInner ($a, $b, $c)
    {
        write-host "a=" $a
        write-host "b=" $b
        write-host "c=" $c
    }
    
    function Test ($hash) {
        return TestInner @hash
    }
    
    Test (Get-TestArgs)
    TestInner 1 2 3
    
  2. Design your functions so that each takes a hashtable as the only parameter - it's a simplified version of 1. It looks a little ugly - you cannot determine the required parameters to Test just by looking at its declaration.

    function Test ($hash)
    {
        write-host "a=" $hash.a
        write-host "b=" $hash.b
        write-host "c=" $hash.c
    }
    
    Test (Get-TestArgs)
    
  3. Use the approach from 2., but create a new type for the function parameter, to be a little more "type safe":

    add-type -TypeDefinition @"
        public class TestArg {
            public int a = 0;
            public int b = 0;
            public int c = 0;
        }
    "@
    
    function Get-TestArgs() { return new-object -type "TestArg" -Property @{a=1;b=2;c=3} }
    
    function Test ([TestArg] $o)
    {
        write-host "a=" $o.a
        write-host "b=" $o.b
        write-host "c=" $o.c
    }
    
    
    Test (Get-TestArgs) 
    
  4. Take advantage of parameter sets and pipelines. This is similar to writing a wrapper, but the target function is also the wrapper - depending on the parameter set, it will either execute normally or use splatting to invoke itself again, but with different parameter set.

    function Test {
    param(
        [Parameter(ParameterSetName="set1",Position=0)][int]$a, 
        [Parameter(ParameterSetName="set1",Position=1)][int]$b, 
        [Parameter(ParameterSetName="set1",Position=2)][int]$c, 
        [Parameter(ParameterSetName="pipeline",ValueFromPipeLine=$true,Position=0)][Hashtable]$obj
    )
    
        if ($obj -ne $null) { return Test @obj }
    
        write-host "a=" $a
        write-host "b=" $c
        write-host "c=" $c
    
    }
    
    Test (Get-TestArgs)
    Get-TestArgs | Test        
    Test 1 2 3
    

Depending on how many functions you need to create and how are you intend to invoke them, you could choose one of these solutions. Personally, the last one seems the most elegant choice to me. And I would go with pipes: Get-TestArgs | Test looks more natural in PowerShell than Test (Get-TestArgs). For example, many of Azure PowerShell commands are designed to work that way.

like image 59
qbik Avatar answered Sep 28 '22 00:09

qbik


I'm fairly certain this can't be done with splatting because:

[Splatting] says, “Take whatever characters come next and assume they’re a variable name. Assume that the variable contains a hashtable, and that the keys are parameter names. Expand those out, and feed the values from the hashtable to those parameters.” That may sound like a long-winded explanation, but that’s what’s happening.

I really think the best you'll be able to do is:

function Test() {
    param($a, $b, $c)
    Write-Host 'A' $a
    Write-Host 'B' $b
    Write-Host 'C' $c
}

function Get-TestArgs() { @(1,2,3) }

$p = Get-TestArgs; Test @p
like image 27
Dave Markle Avatar answered Sep 28 '22 02:09

Dave Markle