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.