Home > other >  Calling a function variably
Calling a function variably

Time:10-08

I have several PS1s with functions in them in the format "Invoke-T1234", "Invoke-T1235", etc. I'd like to create a function in a PS Module that allows the user to call one of these PS1s through an argument, e.g. "Invoke-Script -Script T1234" and it then runs "Invoke-T1234". Is it possible to pass in a variable to the function that calls the scripts so I don't have to create several "If" clauses?

E.g.

function Invoke-Script {
        param (
        [Parameter(Mandatory = $false)]       
            [string]$Script= ""
        )
    If ($Script){
    Invoke-$Script    
    }

Obviously this isn't working, as PowerShell is interpreting $Script literally instead of the value.

I know I can do

If ($Script -eq T1234){
Invoke-T1234}

But there are many PS1s, so I'd like to do this in the least amount of code possible.

CodePudding user response:

You could use Get-Command to discover the target function based on the argument:

function Invoke-TScript
{
  param(
    [int]$ScriptID
  )

  $cmd = Get-Command "Invoke-T${ScriptID}" -ErrorAction SilentlyContinue
  if($cmd){
    & $cmd @args
  }
  else {
    Write-Error "No function named 'Invoke-T${ScriptID}' available"
  }
}

Splatting the automatic $args variable inside the function means any additional parameter arguments will be passed to the target script.

Now you can do:

Invoke-TScript -ScriptID 1234 -ScriptParam 'script argument'

which will then execute:

Invoke-T1234 -ScriptParam 'script argument'

CodePudding user response:

Using Get-Command to discover and test the existence of commands, as shown in Mathias R. Jessen's helpful answer, is definitely an option, but if you know the specific name of the command (function), as in your case, it is sufficient to use &, the call operator, which can invoke commands specified via a(n expandable) string, variable or expression:

# Use an expandable string to construct the target command name
# and invoke it with &
# If needed, pass arguments as you usually would; e.g.
#   & "Invoke-$Script" foo bar
& "Invoke-$Script"
  • You don't even strictly need quoting (the enclosing "...") here: & Invoke-$Script will do.

    • Because the arguments passed to & are parsed in argument(-parsing) mode, PowerShell generally treats unquoted arguments that include variable references (such as Invoke-$Script) implicitly like an expandable string, i.e. as if they had explicitly been enclosed in "...". However, there are pitfalls, so it's a good habit to form to use "..." explicitly - see this answer for background information.
  • Conversely, when quoting is used, use of & is required, even with verbatim strings, whereas unquoted, verbatim command names and paths do not require & (but may be used with it); e.g. c:\foo\script.ps1 works without & too, but
    & 'c:\foo\script file.ps1' requires &. See this answer for background information.

In the context of your code, with an error handler:

function Invoke-Script {
        param (
        [Parameter(Mandatory = $false)]       
            [string]$Script= ""
        )
    if ($Script) {
      try {
        & "Invoke-$Script"
      } catch {
        throw "Failed to call 'Invoke-$Script': $_"
      }
    }
}
  • Related