Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating functions dynamically in a module in PowerShell

Suppose I have the following code in a module (called MyModule.psm1, in the proper place for a module):

function new-function{

    $greeting='hello world'
    new-item -path function:\ -name write-greeting -value {write-output $greeting} -Options AllScope
    write-greeting
}

After importing the module and running new-function I can successfully call the write-greeting function (created by new-function).

When I try to call the write-greeting function outside the scope of the new-function call, it fails because the function does not exist.

I've tried dot-sourcing new-function, but that doesn't help. I've supplied the -option Allscope, but apparently that only includes it in child scopes.

I've also tried explicitly following the new-item call with an export-modulemember write-greeting which doesn't give an error, but also doesn't create the function.

I want to be able to create a function dynamically (i.e. via new-item because the contents and name of the function will vary based on input) from a function inside a module and have the newly created function available to call outside of the module.

Specifically, I want to be able to do this:

Import-module MyModule
New-Function
write-greeting

and see "hello world" as output

Any ideas?

like image 720
Mike Shepard Avatar asked Jan 24 '16 21:01

Mike Shepard


1 Answers

Making the function visible is pretty easy: just change the name of your function in New-Item to have the global: scope modifier:

new-item -path function:\ -name global:write-greeting -value {write-output $greeting} #-Options AllScope

You're going to have a new problem with your example, though, because $greeting will only exist in the new-function scope, which won't exist when you call write-greeting. You're defining the module with an unbound scriptblock, which means it will look for $greeting in its scope (it's not going to find it), then it will look in any parent scopes. It won't see the one from new-function, so the only way you'll get any output is if the module or global scope contain a $greeting variable.

I'm not exactly sure what your real dynamic functions will look like, but the easiest way to work around the new issue is to create a new closure around your scriptblock like this:

new-item -path function:\ -name global:write-greeting -value {write-output $greeting}.GetNewClosure()

That will create a new dynamic module with a copy of the state available at the time. Of course, that creates a new problem in that the function won't go away if you call Remove-Module MyModule. Without more information, I'm not sure if that's a problem for you or not...

like image 145
Rohn Edwards Avatar answered Sep 28 '22 10:09

Rohn Edwards