1

I want a function to pass all of it arguments to another function. Is there a better way of doing this? My version doesn't seem to work right however.

function remote {
    $fargs = $MyInvocation.UnboundArguments

    remote_version1 @fargs      
}

function remote_version1 {
param(
    [alias("m", "computer")]    
    [Parameter(Mandatory=$false)]   [string]$Machine  = "",
    [Parameter(Mandatory=$false)]   [string]$loc_file = "",
    [Parameter(Mandatory=$false)]   [string]$rem_file = "",
    [Parameter(Mandatory=$false)]   [switch]$tmp,
    [Parameter(Mandatory=$false)]   [switch]$banner,
    [Parameter(Mandatory=$false)]   [int]$profile     = $script:remote_profile,  
    [Parameter(Mandatory=$false)]   [int]$Hop         = $script:remote_hop        # Second Hop Mode     
)
    #write-host $MyInvocation.BoundParameters   
    write-host "loc_file: $loc_file"
    write-host "rem_file: ($rem_file)"
}

$common_flags              = @{}
$common_flags["Banner"]    = $true
$common_flags["Machine"]   = "mymachine"
$common_flags["Hop"]       = $true
$common_flags["Profile"]   = $true
remote @common_flag -loc_file .\file.txt  -tmp

Result:

loc_file: .\file.txt
rem_file: True   ##<==== Why True?  SHould be "" since 
                 ##      it isn't set when called?? 

(I can't seem to reproduce the output by snipping the code out of script.. makes me think powershell has some type of pointer bug? But this is the problem i'm having... $rem_file is set to a boolean for some reason, when its a string...)

Maybe i'm passing the wrong type into the "splat operator" @, and the orderingof items is random in the hash? but powershell doesn't seem to complain its the wrong object type?

Is there a way to convert a string into a company and execute it instead of using splat?

pico
  • 1,660
  • 4
  • 22
  • 52
  • If you can't reproduce the problem, how can you still have the problem? To pass _all_ arguments, do `targetCommand @PSBoundParameters @args` – Mathias R. Jessen Oct 27 '21 at 13:24
  • I don't know, sorry, I just found a work around using invoke-expression instead. – pico Oct 27 '21 at 13:31
  • I don't *think* this has anything to do with your issue but, in powershell, when an optional string parameter is not passed to a function its value is always "" - so explicitly defining an empty string as a fallback is redundant. – diopside Oct 27 '21 at 19:08
  • Are you explicitly naming every parameter when providing them as arguments to the remote function? If not I would try that. It sounds like the string is being cast to a bool - which means powershell is probably trying to deduce what the parameters are purely from their order in the list and assuming its one of the switch params. – diopside Oct 27 '21 at 19:19

2 Answers2

2

The automatic $args variable has built-in magic when used for splatting (@args), allowing it to pass named arguments through properly - note that this does not work with any other arrays, whether self-created array or returned from $MyInvocation.UnboundArguments.

Therefore, the following should meet your needs:

function remote {
  # @args passes both named and positional (unnamed) arguments through.
  remote_version1 @args      
}

Note:

  • If the relaying function (remote) itself had declared parameters (with a param(...) block), you could also relay them with @PSBoundParameters, via automatic $PSBoundParameters variable.

  • If a relaying function with declared parameters is non-advanced, you may use @PSBoundParameters in addition to passing unbound arguments through with @args, whereas advanced functions by definition prevent passing unbound arguments (in other words: they do not support $args).

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    very useful knowledge! Until now I thought splatting always had to be used w/ a previously explicitly declared hashtable of params – diopside Oct 27 '21 at 22:30
  • Glad to hear it, @diopside. _Any variable_ - whether automatic (built in) or user-defined - can be used for splatting, by prefixing its name with `@` instead of `$`: If it contains an array or array-like collection, each element is passed as a _positional_ (unnamed) argument. If it contains a hashtable or hashtable-like data structure (a type that implements `[System.Collections.IDictionary]`), each entry is passed as a _named_ argument, where the entry's _key_ names the target parameter (without the leading `-`), and the _value_ is its argument. A scalar (single) value is passed as-is. – mklement0 Oct 28 '21 at 04:03
-2

UnboundArguments is an Array instead of a Hash. I simply print the Array to convert it to a string and feed it to invoke-expression.

        $fargs = $MyInvocation.UnboundArguments
        $fargs = $fargs -replace "True", "`$true"
        $fargs = $fargs -replace "False", "`$false"
        invoke-expression  "remote_version1 $fargs"
pico
  • 1,660
  • 4
  • 22
  • 52
  • The obligatory warning: `Invoke-Expression` (`iex`) should generally be _avoided_ and used only as a _last resort_, due to its inherent security risks. Superior alternatives are usually available. If there truly is no alternative, only ever use it on input you either provided yourself or fully trust - see [this answer](https://stackoverflow.com/a/51252636/45375). – mklement0 Oct 27 '21 at 21:46
  • 1
    Security risks aside, your solution has severe limitations, such as not being able to relay arguments with embedded spaces or arguments with types other than strings or numbers. – mklement0 Oct 27 '21 at 21:47