1

Say I'm passing a function as a parameter, is there a way to find out the name of the passed function through introspection in powershell? Or do I just have to pass it along with the rest of the parameters?

(without calling the function in question)

leeand00
  • 25,510
  • 39
  • 140
  • 297

1 Answers1

1

The linked question tries to pass a function by name, as a string, in which case the answer is obvious: the argument itself is the function name.

In case a script block is passed instead, you can use the following technique:

function get-ScriptBlockCommandName {

  param(
   [scriptblock] $ScriptBlock,
   [switch] $Expand 
  )

  # Using the script block's AST, extract the first command name / path token.
  $commandName = $ScriptBlock.Ast.EndBlock.
    Statements[0].PipelineElements.CommandElements[0].Extent.Text

  # Expand (interpolate) the raw name, if requested.
  if ($Expand) {
    $commandName = $ExecutionContext.InvokeCommand.ExpandString($commandName) 
  }

  # Remove outer quoting, if present.
  if ($commandName -match '^([''"])(.+)\1$') {
    $commandName = $Matches[2]
    if ($Matches[1] -eq "'") { $commandName = $commandName -replace "''", "'" }
  } 

  # Output
  $commandName

}

The function returns the (first) command name / path that is called from inside the script block.

Caveats:

  • An error will occur if you pass an expression (e.g., 1 + 2) as the first statement inside the script block.

  • Only the first command is analyzed (and its command name / path returned), whereas there is no limit to how many statements you can place inside a script block.

  • By default, if the command name / path is constructed from variables / other commands, these are not expanded (interpolated), given that doing so can result in execution of commands; to opt into expansion, use the -Expand switch.

Example calls:

PS> get-ScriptBlockCommandName { foo -bar baz -more stuff }
foo

This also works with quoted names / paths (note how & must then be used to invoke the command):

PS> get-ScriptBlockCommandName { & '/dir name/foo' -bar baz -more stuff }
/dir name/foo

However, to avoid potentially unwanted execution of commands, the command name / path is returned as-is, with variable references and subexpressions unexpanded.
You can opt to have these expanded by passing -Expand:

PS> get-ScriptBlockCommandName { & "$HOME/scripts/foo.ps1" -bar baz  } -Expand
C:/Users/jdoe/scripts.ps1  # e.g.
mklement0
  • 382,024
  • 64
  • 607
  • 775