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 asInvoke-$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.
- Because the arguments passed to
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': $_"
}
}
}