tellingmachine
tellingmachine

Reputation: 1085

How do I dynamically create functions that are accessible in a parent scope?

Here is an example:

function ChildF()
{
  #Creating new function dynamically
  $DynFEx =
@"
  function DynF()
  {
    "Hello DynF"
  }
"@
  Invoke-Expression $DynFEx
  #Calling in ChildF scope Works
  DynF 
}
ChildF
#Calling in parent scope doesn't. It doesn't exist here
DynF

I was wondering whether you could define DynF in such a way that it is "visible" outside of ChildF.

Upvotes: 9

Views: 4608

Answers (5)

Kirt Carson
Kirt Carson

Reputation: 97

Thanks to Richard's post. Kept having issues doing this simple thing. I revised for passing a function from local to remote.

#Method 1 Load the function from disk    
$getCert = gc 'C:\MyScripts\getCert.ps1'
Invoke-Command $RemoteSrv -ScriptBlock {Set-Variable -name DefFN -value ($Args -join "`n") -scope global ; Invoke-Expression $DefFn } -ArgumentList $getCert
    
#Method 2 Load the function from local definition of function
Invoke-Command $RemoteSrv -ScriptBlock {Set-Variable -name DefFN -value ($Args -join "`n") -scope global ; Invoke-Expression $DefFn } -ArgumentList ('Function GetCert {'+(Get-Command GetCert).Definition+'}')

#Remote server now has function
Invoke-Command $RemoteSrv -ScriptBlock {getcert stackoverflow.com}

URL        : stackoverflow.com
Expires    : 12/14/2021 8:07:08 AM
SAN        : DNS Name=*.askubuntu.com, DNS Name=.....
Thumbprint : ec0055be478411bafe98d11d63a5c9279ff0e173
IP         : 151.101.193.69
Handle     : 2866249748176
Issuer     : CN=R3, O=Let's Encrypt, C=US
Subject    : CN=*.stackexchange.com

Upvotes: 0

Intrepidis
Intrepidis

Reputation: 2980

A more correct and functional way to do this would be to return the function body as a script block and then recompose it.

function ChildF() {
    function DynF() {
        "Hello DynF"
    }
    return ${function:DynF}
}
$DynFEx = ChildF
Invoke-Expression -Command "function DynF { $DynFEx }"
DynF

Upvotes: 3

Shay Levy
Shay Levy

Reputation: 126792

You can scope the function with the global keyword:

function global:DynF {...}

Upvotes: 9

Steven Murawski
Steven Murawski

Reputation: 11270

Another option would be to use the Set-Item -Path function:global:ChildFunction -Value {...}

Using Set-Item, you can pass either a string or a script block to value for the function's definition.

Upvotes: 14

Richard Berg
Richard Berg

Reputation: 20782

The other solutions are better answers to the specific question. That said, it's good to learn the most general way to create global variables:

# inner scope
Set-Variable -name DynFEx -value 'function DynF() {"Hello DynF"}' -scope global

# somewhere other scope
Invoke-Expression $dynfex
DynF

Read 'help about_Scopes' for tons more info.

Upvotes: 9

Related Questions